洛谷P4095||bzoj3163 [HEOI2013]Eden 的新背包问题
https://www.luogu.org/problemnew/show/P4095
不太会。。
网上有神奇的做法:
第一种其实是暴力(复杂度3e8...)然而可以A。考虑多重背包,发现没有办法快速删除某个物品造成的贡献。考虑对于每个i,求出an1[i]和an2[i],分别表示对于[1,i]和[i,n]区间内所有物品的答案数组(如an1[i][j]表示[1,i]区间内用掉容量j可以带来的最大贡献),这个就是用普通多重背包求出来(可能要优化一下多重背包,以下用了二进制优化)。每次询问(x,y),就暴力枚举在[1,x-1]和[x+1,n]区间内分别取多少容量的物品,取最大贡献。复杂度O(n*log(c)*e+q*e)(如果用二进制优化多重背包)
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<vector> 5 using namespace std; 6 #define fi first 7 #define se second 8 #define mp make_pair 9 #define pb push_back 10 typedef long long ll; 11 typedef unsigned long long ull; 12 typedef pair<int,int> pii; 13 int n,n1; 14 int c[10010],d[10010]; 15 int lp[1010],rp[1010]; 16 void work(int x,int y,int z) 17 { 18 //printf("1t%d %d %d %d\n",x,y,z,k); 19 ++n1;x*=z;y*=z; 20 c[n1]=x;d[n1]=y; 21 //printf("1t%d %d\n",c[n1],d[n1]); 22 } 23 int qq; 24 int ans;//int ans[10010]; 25 int an1[10010][1010];//an1[i][j]表示(前i个物品)j的容量最大价值 26 int an2[10010][1010];//i及之后的物品... 27 int main() 28 { 29 int i,j,x,y,z; 30 scanf("%d",&n); 31 for(i=1;i<=n;++i) 32 { 33 scanf("%d%d%d",&x,&y,&z); 34 lp[i]=n1+1; 35 for(j=1;(j<<1)-1<=z;j<<=1) 36 work(x,y,j); 37 if(z-j+1) work(x,y,z-j+1); 38 rp[i]=n1; 39 } 40 for(i=1;i<=n1;++i) 41 { 42 memcpy(an1[i],an1[i-1],sizeof(an1[i])); 43 for(j=1000;j>=c[i];--j) 44 an1[i][j]=max(an1[i][j],an1[i][j-c[i]]+d[i]); 45 } 46 for(i=n1;i>=1;--i) 47 { 48 memcpy(an2[i],an2[i+1],sizeof(an2[i])); 49 for(j=1000;j>=c[i];--j) 50 an2[i][j]=max(an2[i][j],an2[i][j-c[i]]+d[i]); 51 } 52 scanf("%d",&qq); 53 for(i=1;i<=qq;++i) 54 { 55 scanf("%d%d",&x,&y);++x; 56 ans=0; 57 for(j=0;j<=y;++j) 58 ans=max(ans,an1[rp[x-1]][j]+an2[lp[x+1]][y-j]); 59 printf("%d\n",ans); 60 } 61 return 0; 62 }
第二种是神奇的分治。注意到既不能快速删除多重背包中某个物品造成的贡献,又不能快速合并两个多重背包,但是可以快速向多重背包中加入一个物品。分治时维护一个多重背包的答案数组。solve(l,r),就先把[l,mid]内部的物品加入多重背包,然后solve(mid+1,r),再去掉[l,mid]内部物品造成的贡献(只需要恢复这一步操作之前的贡献数组即可),然后将[mid+1,r]内部的物品加入多重背包,solve(l,mid),去掉[mid+1,r]内部造成的贡献。这样当进行到solve(l,l)时就恰好只有l这个物品自身没有加入多重背包了,此时对于所有对这个位置的询问处理一下即可。复杂度O(n*log(c)*e*log(n)+q)(如果用二进制优化多重背包)(然而以下代码洛谷上跑的比“暴力”慢???)
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<vector> 5 using namespace std; 6 #define fi first 7 #define se second 8 #define mp make_pair 9 #define pb push_back 10 typedef long long ll; 11 typedef unsigned long long ull; 12 typedef pair<int,int> pii; 13 int n,n1; 14 int c[10010],d[10010]; 15 int lp[1010],rp[1010]; 16 void work(int x,int y,int z) 17 { 18 ++n1;x*=z;y*=z; 19 c[n1]=x;d[n1]=y; 20 } 21 vector<pii> q[1010]; 22 int qq; 23 int an[1010];int ans[300010]; 24 void solve(int l,int r) 25 { 26 int i,j; 27 if(l==r) 28 { 29 for(i=0;i<q[l].size();++i) 30 ans[q[l][i].se]=an[q[l][i].fi]; 31 return; 32 } 33 int mid=l+((r-l)>>1); 34 int an2[1010]; 35 memcpy(an2,an,sizeof(an2)); 36 for(i=lp[l];i<=rp[mid];++i) 37 for(j=1000;j>=c[i];--j) 38 an[j]=max(an[j],an[j-c[i]]+d[i]); 39 solve(mid+1,r); 40 memcpy(an,an2,sizeof(an)); 41 for(i=lp[mid+1];i<=rp[r];++i) 42 for(j=1000;j>=c[i];--j) 43 an[j]=max(an[j],an[j-c[i]]+d[i]); 44 solve(l,mid); 45 memcpy(an,an2,sizeof(an)); 46 } 47 int main() 48 { 49 int i,j,x,y,z; 50 scanf("%d",&n); 51 for(i=1;i<=n;++i) 52 { 53 scanf("%d%d%d",&x,&y,&z); 54 lp[i]=n1+1; 55 for(j=1;(j<<1)-1<=z;j<<=1) 56 work(x,y,j); 57 if(z-j+1) work(x,y,z-j+1); 58 rp[i]=n1; 59 } 60 scanf("%d",&qq); 61 for(i=1;i<=qq;++i) 62 { 63 scanf("%d%d",&x,&y);++x; 64 q[x].pb(pii(y,i)); 65 } 66 solve(1,n); 67 for(i=1;i<=qq;++i) 68 printf("%d\n",ans[i]); 69 return 0; 70 }