LOJ6039「雅礼集训 2017 Day5」珠宝

题目

注意到\(c_i\leq 300\)我们显然可以利用\(c_i\)来搞事情

一个自然的想法是我们根据\(c_i\)进行分组,每一个组内物品体积都是一样的,所以按照价值从大到小排序,变成了多个物品,于是我们把问题转化成了一个分组背包问题

于是我们有这样的一个\(dp\)\(dp_{i,j}=\max dp_{i-1,j-k\times i}+w_{i,k}\)\(w_{i,k}\)表示第\(i\)组前\(k\)个物品的价值,但这个分组背包的复杂度还是太高

不难发现,\(j\)\(j-i\times k\)是在\(\mod\ i\)意义下相等的,于是对于同一组我们还可使根据\(\mod\ i\)的值进行分类

还能够发现这个\(dp\)存在决策单调性,如果\(j-k\times i\)\(j-p\times i(p>k)\)\(j\)更优,那么对于更大的\(j\)来说\(j-k\times i\)还是要优于\(j-p\times i\),因为\(w_{i,k}\)差分之后是递减的,\(j-k\times i\)的增长速度快于\(j-p\times i\),所以我们直接分治即可

复杂度是\(O(mc\log m)\)代码

#include<bits/stdc++.h>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
inline int read() {
	char c=getchar();int x=0;while(c<'0'||c>'9')c=getchar();
	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
int n,m,nw,T;
std::vector<LL> w[305];
LL dp[50005],g[50005],f[50005];
inline int cmp(LL A,LL B) {return A>B;}
void solve(int l,int r,int x,int y) {
	if(l>r) return;int mid=l+r>>1;
	f[mid]=g[mid];int id=mid;
	for(re int i=x;i<=y&&i<mid;++i) {
		if(mid-i>w[nw].size()) continue;
		LL k=g[i]+w[nw][mid-i-1];
		if(k>f[mid]) f[mid]=k,id=i;
	}
	if(l==r)return;
	solve(l,mid-1,x,id);solve(mid+1,r,id,y);
}
int main() {
	n=read(),m=read();
	for(re int c,v,i=1;i<=n;i++)c=read(),v=read(),w[c].push_back((long long)v),T=max(c,T);
	for(re int i=1;i<=T;i++) {
		if(!w[i].size()) continue;
		std::sort(w[i].begin(),w[i].end(),cmp);
		for(re int j=1;j<w[i].size();++j) w[i][j]+=w[i][j-1];
	}
	for(re int i=T;i;i--) {
		if(!w[i].size())continue;nw=i;
		for(re int tot=0,j=0;j<i;j++,tot=0) {
			for(re int k=j;k<=m;k+=i) g[++tot]=dp[k];
			solve(1,tot,1,tot);
			for(re int h=1,k=j;k<=m;k+=i,++h) dp[k]=f[h];
		}
		for(re int j=1;j<=m;j++)dp[j]=max(dp[j],dp[j-1]);
	}
	for(re int i=1;i<=m;i++) printf("%lld ",dp[i]);
	return 0;
}
posted @ 2020-01-06 10:38  asuldb  阅读(1003)  评论(4编辑  收藏  举报