【题解】CF1442D Sum
题意
给定 \(n\) 个不降的数组。有一个值 \(ans\),初始为 \(0\)。你需要进行如下操作 \(k\) 次:
选择一个数组,把 \(ans\) 加上数组的第一个元素,之后把它删除。
请求出 \(ans\) 最大是多少。
所有元素总个数 \(\leq 10^6 \space n,k\leq 3000 \space 0 \leq a_{i,j} \leq 10^8\)
题解
显然有一个结论
最多只有一个组会取一部分,其他组要么一个都不取,要么都取完
比较显然,感性理解一下,因为是不降的,如果有两个没取完,那么显然取完其中的一个会更优
然后如果枚举没取完的,那么两边背包合并还是 \(O(nk^2)\),相当于没有优化
考虑分治 \(solve(l,r)\) 代表没取完的在 \([l,r]\) 中,所以 \([1,l)\) 和 \((r,n]\) 都是可以直接背包的
调用 \(solve(l,mid)\) 时就要把 \(mid+1~r\) 的加到背包,\(solve(mid+1,r)\) 同理
记得要回滚
Code
#include <bits/stdc++.h>
using namespace std;
#define FOR(i,j,k) for(int i=j; i<=k; ++i)
#define ROF(i,j,k) for(int i=j; i>=k; --i)
inline int read (void) {
int x = 0, f = 1, ch = getchar();
while(!isdigit(ch)) { if(ch == '-') f = -f; ch = getchar(); }
while(isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
const int maxn = 3005;
vector <int> v[maxn];
long long ans, tot[maxn], f[maxn];
long long now, sta[maxn*maxn*2][2];
int k, sz[maxn];
inline void roll_back (int flag) {
while(now > flag) {
f[sta[now][0]] = sta[now][1];
-- now;
}
}
void solve (int l, int r) {
if(l == r) {
long long sum = 0;
ans = max(ans, f[k]);
FOR(i,0,sz[l]-1) if(i < k) ans = max(ans, (sum += v[l][i]) + f[k - i - 1]);
return ;
}
int flag = now, mid = (l + r >> 1);
FOR(i,mid+1,r)
ROF(j,k,sz[i])
if(f[j] < f[j - sz[i]] + tot[i]) {
sta[++ now][0] = j;
sta[now][1] = f[j];
f[j] = f[j - sz[i]] + tot[i];
}
solve (l, mid);
roll_back (flag);
FOR(i,l,mid)
ROF(j,k,sz[i])
if(f[j] < f[j - sz[i]] + tot[i]) {
sta[++ now][0] = j;
sta[now][1] = f[j];
f[j] = f[j - sz[i]] + tot[i];
}
solve (mid+1, r);
roll_back (flag);
}
int main (void) {
int n = read(); k = read();
FOR(i,1,n) {
sz[i] = read();
FOR(j,1,sz[i]) {
int x = read();
tot[i] += x;
v[i].push_back(x);
}
}
solve (1, n);
printf("%lld\n", ans);
return 0;
}

浙公网安备 33010602011771号