题解 [CF1442D] Sum
以为是个高端技巧/分治/凸包/多项式题
结果是个结论题
话说我以前做过这个题来着现在还是啥也不会
结论是最多有一个序列被选且被选完
证明考虑若有两个没有被选完的序列 \(a, b\)
一个选到了 \(i\),一个到了 \(j\),且 \(a_i\geqslant b_j\)
那么有 \(a_{i+1}\geqslant a_i\geqslant b_j\geqslant b_{j-1}\)
也就是说 \(b\) 数组少选一个留位置给 \(a\) 一定是不劣的
那么就是有 \(n\) 个物品,要对每个物品分别查一次除了这个物品以外的所有物品形成的背包
那么分治即可,向左/右区间递归前加入另一个区间中的元素
复杂度 \(O(nk\log n)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 3010
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, k;
int siz[N];
vector<ll> a[N];
ll f[N], sum[N], ans;
void solve(int l, int r) {
if (l==r) {
ll pre=0;
for (int i=0; i<=siz[l]; ++i) ans=max(ans, (pre+=a[l][i])+f[k-i]);
return ;
}
ll bkp[N];
int mid=(l+r)>>1;
for (int i=0; i<=k; ++i) bkp[i]=f[i];
for (int i=mid+1; i<=r; ++i)
for (int j=k; j>=siz[i]; --j)
f[j]=max(f[j], f[j-siz[i]]+sum[i]);
solve(l, mid);
for (int i=0; i<=k; ++i) f[i]=bkp[i];
for (int i=l; i<=mid; ++i)
for (int j=k; j>=siz[i]; --j)
f[j]=max(f[j], f[j-siz[i]]+sum[i]);
solve(mid+1, r);
}
signed main()
{
n=read(); k=read();
for (int i=1; i<=n; ++i) {
a[i].resize((siz[i]=read())+1);
for (int j=1; j<=siz[i]; ++j) a[i][j]=read();
siz[i]=min(siz[i], k);
for (int j=1; j<=siz[i]; ++j) sum[i]+=a[i][j];
}
solve(1, n);
printf("%lld\n", ans);
return 0;
}