题解 [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;
}
posted @ 2022-06-11 19:25  Administrator-09  阅读(4)  评论(0编辑  收藏  举报