Codeforces 1442D

Codeforces 题面传送门 & 洛谷题面传送门

智商掉线/ll

本来以为是个奇怪的反悔贪心,然后便一直往反悔贪心的方向想就没想出来,看了题解才发现是个 nb 结论题。

Conclusion. 在最优方案中,至多只有一个数组只有部分被选,其余数组要么全选要么全都不选。

证明:考虑调整。假设存在两个数组 (x,y) 分别选了前 (p,q) 个元素,这里不妨假设 (a_{x,p+1}ge a_{y,q+1}),那么考虑从 (y) 数组中拎 (l=min(len_x-p,q)) 个元素到 (x) 中,那么答案的增量

[Delta=sumlimits_{i=1}^la_{x,p+i}-sumlimits_{i=1}^la_{y,q-i+1} ]

由于 (a_x,a_y) 均为单调数组并且 (a_{x,p+1}ge a_{y,q+1}),故 (a_{x,p+i}ge a_{y,q-i+1}),因此 (Deltage 0) 必然成立。如此调整下去即可得证。

有了这个性质之后如何求解答案呢?一个很自然的想法是对前后缀分别跑一遍背包,然后枚举那个选了一部分的数组及其选择的长度,然后用背包合并的技巧合并。但这样复杂度是三方的,如果将 (n,k) 视作同阶。一脸过不去的样子。考虑优化,注意到对于背包而言,其合并的复杂度可能很高,达到平方,但插入的复杂度并不算高,因此考虑分治,具体来说当分治一个区间时,我们记 (mid=lfloordfrac{l+r}{2} floor),然后将 ([l,mid]) 中数组插入背包,分治 ([mid+1,r]),然后将背包还原成原来的样子,插入 ([mid+1,r]) 中的数组,分治 ([l,mid]),再复原即可实现 (nklog n) 的复杂度。

感觉我博客里有 114514191981019260817998244353 个“找性质”啊,为啥我这类找性质的题目还是做不出来呢?zibile,zabanna/dk

const int MAXN=3000;
int n,k,len[MAXN+5];
ll sum[MAXN+5],res=0;
vector<int> a[MAXN+5];
struct knap{
	ll dp[MAXN+5];
	void clear(){memset(dp,0,sizeof(dp));}
	knap(){clear();}
	void insert(int v,ll w){for(int i=k;i>=v;i--) chkmax(dp[i],dp[i-v]+w);}
};
knap cur;
void solve(int l,int r){
	if(l==r){
		ll s=0;
		for(int i=0;i<=a[l].size();i++){
			if(k>=i) chkmax(res,s+cur.dp[k-i]);
			if(i!=a[l].size()) s+=a[l][i];
		} return;
	} int mid=l+r>>1;knap tmp=cur;
	for(int i=l;i<=mid;i++) cur.insert(len[i],sum[i]);
	solve(mid+1,r);cur=tmp;
	for(int i=mid+1;i<=r;i++) cur.insert(len[i],sum[i]);
	solve(l,mid);cur=tmp;
}
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++){
		scanf("%d",&len[i]);
		for(int j=1,x;j<=len[i];j++){
			scanf("%d",&x);sum[i]+=x;
			a[i].pb(x);
		}
	} solve(1,n);
	printf("%lld
",res);
	return 0;
}
原文地址:https://www.cnblogs.com/ET2006/p/Codeforces-1442D.html