[题解] [CF500F] New Year Shopping
Posted ztlztl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[题解] [CF500F] New Year Shopping相关的知识,希望对你有一定的参考价值。
题面
题解
提供两种方法
线段树分治
将一个物品可以购买的时间区间打到线段树上
考虑对于每一个点如何算贡献
从线段树的根开始做 01 背包
向下递归时记得撤销不同区间的影响
这样每一次询问只会算 (log(t)) 次, 每一个物品, 只会在 (log(t)) 段区间中被计算
每次计算的复杂度是 (O(max(b)))
所以总的复杂度就是 (O(n*log(t)*b)), 可以通过这道题
Code1
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
const int N = 2e4 + 5;
const int INF = 0x3f3f3f3f;
#define pii pair<int, int>
using namespace std;
int n, p, c[N], h[N], t[N], m, f[N], ans[N], lim;
vector<pii > vec1[N * 16], vec2[N * 16];
template < typename T >
inline T read()
{
T x = 0, w = 1; char c = getchar();
while(c < ‘0‘ || c > ‘9‘) { if(c == ‘-‘) w = -1; c = getchar(); }
while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = getchar();
return x * w;
}
void insert(int p, int l, int r, int ql, int qr, int c, int v)
{
if(ql <= l && r <= qr)
return (void) (vec1[p].push_back(make_pair(c, v)));
int mid = (l + r) >> 1;
if(ql <= mid) insert(p << 1, l, mid, ql, qr, c, v);
if(mid < qr) insert(p << 1 | 1, mid + 1, r, ql, qr, c, v);
}
void modify(int p, int l, int r, int k, int sz, int x)
{
vec2[p].push_back(make_pair(x, sz));
if(l == r) return;
int mid = (l + r) >> 1;
if(k <= mid) modify(p << 1, l, mid, k, sz, x);
else modify(p << 1 | 1, mid + 1, r, k, sz, x);
}
void solve(int p, int l, int r)
{
int sz = vec1[p].size();
for(int c, v, i = 0; i < sz; i++)
{
c = vec1[p][i].first, v = vec1[p][i].second;
for(int j = 4000; j >= c; j--)
f[j] = max(f[j], f[j - c] + v);
}
for(int i = 1; i <= 4000; i++)
f[i] = max(f[i], f[i - 1]);
sz = vec2[p].size();
for(int tmp, i = 0; i < sz; i++)
ans[tmp = vec2[p][i].first] = max(ans[tmp], f[vec2[p][i].second]);
if(l == r) return;
int mid = (l + r) >> 1, g[4000];
for(int i = 0; i <= 4000; i++)
g[i] = f[i];
solve(p << 1, l, mid);
for(int i = 0; i <= 4000; i++)
f[i] = g[i];
solve(p << 1 | 1, mid + 1, r);
for(int i = 0; i <= 4000; i++)
f[i] = g[i];
}
int main()
{
n = read <int> (), p = read <int> ();
for(int i = 1; i <= n; i++)
c[i] = read <int> (), h[i] = read <int> (), t[i] = read <int> (), lim = max(lim, t[i]);
lim = lim + p - 1;
for(int i = 1; i <= n; i++)
insert(1, 1, lim, t[i], t[i] + p - 1, c[i], h[i]);
m = read <int> ();
for(int a, b, i = 1; i <= m; i++)
{
a = read <int> (), b = read <int> ();
if(a > lim) { ans[i] = 0; continue; }
modify(1, 1, lim, a, b, i);
}
solve(1, 1, lim);
for(int i = 1; i <= m; i++)
printf("%d
", ans[i]);
return 0;
}
将序列分块之后背包
不妨将题目意思转化, 考虑到每一个物品存在的时间区间长度都是一样的
那对于一次询问 ((a, b)) 只有最早出现的时间在 ([a - p, a]) 中的物品才会贡献这次询问
考虑将序列分为一个个大小为 (p) 的块, 总共有 (frac{max(t)}{p}) 个块
将所有块的端点叫做关键点
那么一次询问查询的区间包含且仅包含一个关键点
且只有这个关键点左边的块中的一个后缀和右边的块中的一个前缀才能贡献他
考虑对于每一个关键点, 对于他前面一个块跑一遍 01 背包, 对于他后面一个块跑一遍 01 背包
查询的时候枚举关键点前面的部分用多少钱, 所有的情况取 (max) 即可
复杂度是 (O(nt)) 的
不过这个方法挺仙的啊
Code2
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
const int N = 2e4 + 5;
const int lim = 2e4;
#define pii pair<int, int>
#define mp(i, j) make_pair(i, j)
using namespace std;
int n, p, r[N], l[N], cnt, m, ans;
vector<pii > vec[N];
struct node { int f[4005]; } a[10005];
template < typename T >
inline T read()
{
T x = 0, w = 1; char c = getchar();
while(c < ‘0‘ || c > ‘9‘) { if(c == ‘-‘) w = -1; c = getchar(); }
while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = getchar();
return x * w;
}
void calc(int k, int c, int v)
{
for(int i = 4000; i >= c; i--)
a[k].f[i] = max(a[k].f[i], a[k].f[i - c] + v);
}
int main()
{
n = read <int> (), p = read <int> ();
for(int c, h, t, i = 1; i <= n; i++)
{
c = read <int> (), h = read <int> (), t = read <int> ();
vec[t].push_back(mp(c, h));
}
for(int sz, i = 1; i <= lim; i += p)
{
for(int j = 0; j < p && i + j <= lim; j++)
{
if(j) r[i + j] = r[i + j - 1];
if(vec[i + j].size())
{
sz = vec[i + j].size(), a[++cnt] = a[r[i + j]], r[i + j] = cnt;
for(int k = 0; k < sz; k++)
calc(cnt, vec[i + j][k].first, vec[i + j][k].second);
}
}
for(int j = 1; j < p && i - j >= 1; j++)
{
if(j > 1) l[i - j] = l[i - j + 1];
if(vec[i - j].size())
{
sz = vec[i - j].size(), a[++cnt] = a[l[i - j]], l[i - j] = cnt;
for(int k = 0; k < sz; k++)
calc(cnt, vec[i - j][k].first, vec[i - j][k].second);
}
}
}
m = read <int> ();
int pos, k;
while(m--)
{
pos = read <int> (), k = read <int> (), ans = 0;
for(int i = 0; i <= k; i++)
ans = max(ans, a[l[max(pos - p + 1, 0)]].f[i] + a[r[pos]].f[k - i]);
printf("%d
", ans);
}
return 0;
}
以上是关于[题解] [CF500F] New Year Shopping的主要内容,如果未能解决你的问题,请参考以下文章
CF500F New Year Shopping [线段树分治,背包]
CF1279F New Year and Handle Change 题解
CF1284E New Year and Castle Construction