bzoj3163 [Heoi2013]Eden的新背包问题(分治+背包dp)

每次询问去掉物品i之后,j的钱最多可以买多大价值的东西。
我们可以分治[l,r]表示l~r的物品不买的背包数组。然后递归处理[l,mid]时把[mid+1,r]的物品转移进来,递归处理[mid+1,r]时把[l,mid]的物品转移进来。

转移的时候就是一个多重背包,可以单调队列优化。
复杂度 O ( n m l o g n )

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 1010
#define pa pair<int,int>
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int ans[N][N],n,m=1000,a[N],b[N],c[N],f[11][N];
pa q[N];
inline void Max(int &x,int y){if(x<y) x=y;}
inline void work(int *f,int l,int r){
    for(int i=l;i<=r;++i)
        for(int j=0;j<a[i];++j){
            int qh=1,qt=0;
            for(int k=0;;++k){
                int x=k*a[i]+j;if(x>m) break;
                while(qh<=qt&&k-q[qh].second>c[i]) ++qh;
                while(qh<=qt&&f[x]-k*b[i]>=q[qt].first) --qt;
                q[++qt]=make_pair(f[x]-k*b[i],k);
                Max(f[x],q[qh].first+k*b[i]);
            }
        }
}
inline void solve(int l,int r,int lev){//l~r is unavailable
    if(l==r){memcpy(ans[l],f[lev],sizeof(f[lev]));return;}
    int mid=l+r>>1;
    memcpy(f[lev+1],f[lev],sizeof(f[lev]));
    work(f[lev+1],mid+1,r);solve(l,mid,lev+1);
    memcpy(f[lev+1],f[lev],sizeof(f[lev]));
    work(f[lev+1],l,mid);solve(mid+1,r,lev+1);
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();
    for(int i=1;i<=n;++i) a[i]=read(),b[i]=read(),c[i]=read();
    solve(1,n,0);m=read();
    while(m--){
        int x=read()+1,y=read();
        printf("%d\n",ans[x][y]);
    }return 0;
}

猜你喜欢

转载自blog.csdn.net/icefox_zhx/article/details/80901218