[bzoj4182]Shopping

题目大意

有一颗树,每个节点相当于是一种物品,有三个因素价值、价格、个数。
在树上选一个联通块,然后做多重背包,要求每种物品必须选。
求最大价值。

点分治

我们进行点分治。
对于分治中心,要么在联通块中,要么不在。
不在的情况就是递归继续处理。
在的话,以分治中心为根造一颗树,然后做依赖多重背包。
具体做法是,在dfs序上弄,对于i,选i+1就到i+1,不选直接到i+size[i+1]
现在我们想想多重背包,直接上会TLE。
首先一定要选,就是先强制放一个。
设剩余还有d个,假如d=2^0+2^1+……2^k+t
其中t<2^(k+1)
那么其实可以拆成现在有许多个物品,每种物品只有1个,然后做01背包。
假设原先物品价值为w价格为c
拆出的物品分别是价值为w*2^i价格为c*2^i。
还有一个w*t,c*t的。
于是就是这样。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=500+10,maxm=4000+10;
int f[maxn][maxm],g[maxm];
int h[maxn],go[maxn*2],next[maxn*2],dfn[maxn],a[maxn],size[maxn],w[maxn],c[maxn],d[maxn];
bool bz[maxn];
int i,j,k,l,t,n,m,tot,top,euler,ans,ca;
int read(){
    int x=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}
void add(int x,int y){
    go[++tot]=y;
    next[tot]=h[x];
    h[x]=tot;
}
void dfs(int x,int y){
    top++;
    int t=h[x];
    size[x]=1;
    while (t){
        if (!bz[go[t]]&&go[t]!=y){
            dfs(go[t],x);
            size[x]+=size[go[t]];
        }
        t=next[t];
    }
}
void dg(int x,int y){
    a[++euler]=x;
    size[x]=1;
    int t=h[x];
    while (t){
        if (!bz[go[t]]&&go[t]!=y){
            dg(go[t],x);
            size[x]+=size[go[t]];
        }
        t=next[t];
    }
}
void addin(int w,int c){
    int i;
    fd(i,m,c)
        g[i]=max(g[i],g[i-c]+w);
}
void insert(int x,int w,int c,int d,int y){
    if (c>m) return;
    int i,j;
    fo(i,0,m) g[i]=-100000000;
    fo(i,0,m-c)
        g[i+c]=f[x][i]+w;
    d--;
    if (d){
        j=0;
        while (1){
            if ((1<<j)<=d){
                addin(w*(1<<j),c*(1<<j));
                d-=(1<<j);
            }
            else{
                addin(w*d,c*d);
                break;
            }
            j++;
        }
    }
    fo(i,0,m) f[y][i]=max(f[y][i],g[i]);
}
void dp(){
    if (c[a[1]]>m) return;
    int i,j;
    fo(i,1,top+1)
        fo(j,0,m)
            f[i][j]=-100000000;
    fo(i,1,d[a[1]]) 
        if (c[a[1]]*i<=m) f[1][c[a[1]]*i]=w[a[1]]*i;
    fo(i,1,top){
        if (i==top){
            fo(j,0,m)
                f[top+1][j]=max(f[top+1][j],f[i][j]);
            break;
        }
        insert(i,w[a[i+1]],c[a[i+1]],d[a[i+1]],i+1);
        fo(j,0,m) f[i+size[a[i+1]]][j]=max(f[i+size[a[i+1]]][j],f[i][j]);
    }
    fo(i,0,m) ans=max(ans,f[top+1][i]);
}
void solve(int x){
    top=0;
    dfs(x,0);
    int j=x,k=0,t;
    while (1){
        t=h[j];
        while (t){
            if (!bz[go[t]]&&go[t]!=k&&size[go[t]]>top/2){
                k=j;
                j=go[t];
                break;
            }
            t=next[t];
        }
        if (!t) break;
    }
    euler=0;
    dg(j,0);
    dp();
    bz[j]=1;
    t=h[j];
    while (t){
        if (!bz[go[t]]) solve(go[t]);
        t=next[t];
    }
}
int main(){
    freopen("shopping.in","r",stdin);freopen("shopping.out","w",stdout);
    ca=read();
    while (ca--){
        n=read();m=read();
        fo(i,1,n) h[i]=0;
        tot=0;
        fo(i,1,n) w[i]=read();
        fo(i,1,n) c[i]=read();
        fo(i,1,n) d[i]=read();
        fo(i,1,n-1){
            j=read();k=read();
            add(j,k);add(k,j);
        }
        fo(i,1,n) bz[i]=0;
        ans=0;
        solve(1);
        printf("%d\n",ans);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值