JZOJ4202. Shopping(点分治+树形依赖+多重背包)

题意:

  • 一颗树,每个点代表一个物品,空间 c[i] c [ i ] ,数量 d[i] d [ i ] ,价值 w[i] w [ i ] ,现有一个空间为 m m 的背包,选树上相互连接的物品,求最大价值

想法:

  • 一眼树形背包,时间复杂度上天
  • f[i][j]表示i的子树内,i必选一个,空间为j的最大价值
  • 时间复杂度O( n2m2d n 2 m 2 d
  • 由于选互相连接的物品,所以我们可以假定必须选某个点,然后做一次树形依赖多重背包,最后在递归到子树内继续找
  • 那肯定是找树的重心啊,所以递归成点分治
  • 树形依赖:由于要选某个点必须要选他的所有祖先,所以可以先做出 dfn d f n
  • i=dfn[x] i = d f n [ x ]
  • 选: f[i+1][y]=max(f[i+1][y],f[i][yc[x]]+w[x]) f [ i + 1 ] [ y ] = m a x ( f [ i + 1 ] [ y ] , f [ i ] [ y − c [ x ] ] + w [ x ] )
  • 不选: f[i+siz[x]+1][y]=max(f[i+siz[x]+1][y],f[i][y]) f [ i + s i z [ x ] + 1 ] [ y ] = m a x ( f [ i + s i z [ x ] + 1 ] [ y ] , f [ i ] [ y ] )
  • 多重背包
  • 选d个的话
  • 把它分成 1+2+4+,,,,,+2n+y 1 + 2 + 4 + , , , , , + 2 n + y 的形式,当01背包做,每个物品即空间 c[i]2n c [ i ] ∗ 2 n ,价格 w[i]2n w [ i ] ∗ 2 n
  • 好像还有一种单调队列的套路,找机会学一学
    -时间复杂度( nlognmlogd n l o g n m l o g d
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define fb(i,x) for(i=last[x];i;i=next[i]) 
const int maxN=510,maxM=4010,maxn=-1e9;
using namespace std;
bool bz[maxN],broke[maxN];
int t,n,m,tot,i,w[maxN],c[maxN],d[maxN],f[maxN][maxM],last[maxN],
tov[maxN*2],next[maxN*2],pre[maxN],h[maxM],cnt,siz[maxN],
ans,x,y,cm[maxN],fa[maxN];
void insert(int x,int y){
    tov[++tot]=y,next[tot]=last[x],last[x]=tot;
}
void dfss(int x){
    int i,y;
    bz[x]=true,pre[++cnt]=x,siz[x]=1;
    fb(i,x){
        y=tov[i];
        if (!bz[y] && !broke[y]) dfss(y),siz[x]+=siz[y],fa[y]=x;
    }
}
void find_ans(){
    int x,i,j,xx,yy,k;
    fo(i,1,cnt+1) 
        fo(j,0,m) f[i][j]=maxn;
    f[1][0]=0;
    fo(i,1,cnt){
        x=pre[i];
        fo(j,0,m) h[j]=maxn;
        fo(j,c[x],m) 
            if (f[i][j-c[x]]!=maxn)h[j]=f[i][j-c[x]]+w[x];
        y=d[x]-1; 
        fo (j,0,7){
            if (y<cm[j]) break;
            xx=cm[j]*w[x],yy=cm[j]*c[x];
            fd(k,m,yy)
                if (h[k-yy]!=maxn) h[k]=max(h[k],h[k-yy]+xx);
            y-=cm[j];
        } 
        xx=y*w[x],yy=y*c[x];
        fd(j,m,yy)
            if (h[j-yy]!=maxn) h[j]=max(h[j],h[j-yy]+xx);
        fo(j,c[x],m) f[i+1][j]=max(f[i+1][j],h[j]);
        fo(j,0,m)
            f[i+siz[x]][j]=max(f[i+siz[x]][j],f[i][j]);
    }
    fo(i,0,m)
        ans=max(ans,f[cnt+1][i]);
} 
void dfs(int x){
    int j,cut=0,yy,i;
    bool bz1;
    cnt=0,dfss(x);
    fo(i,1,cnt){
        yy=pre[i],bz1=true;
        if (cnt-siz[yy]>cnt/2) continue;
        fb(j,yy)
            if (siz[tov[j]]>cnt/2 && tov[j]!=fa[yy] && !broke[tov[j]]) {bz1=false;
            break;
        } 
        if (bz1){
            cut=yy;
            break;
        }
    }
    broke[cut]=true;
    fo(i,1,cnt)
        siz[pre[i]]=0,bz[pre[i]]=false,fa[pre[i]]=0;
    cnt=0,dfss(cut);
    find_ans();
    fo(i,1,cnt)
        siz[pre[i]]=0,bz[pre[i]]=false,fa[pre[i]]=0;
    fb(i,cut){
        yy=tov[i];
        if (!broke[yy]) dfs(yy);
    }
}
int main(){
    freopen("shopping.in","r",stdin);
    freopen("shopping.out","w",stdout);
    cm[0]=1;
    fo(i,1,7) cm[i]=cm[i-1]*2;
    for (scanf("%d",&t);t;t--){
        mem(last,0),mem(bz,false),mem(broke,false),tot=0;
        scanf("%d%d",&n,&m);
        fo(i,1,n) scanf("%d",&w[i]);
        fo(i,1,n) scanf("%d",&c[i]);
        fo(i,1,n) scanf("%d",&d[i]);
        fo(i,1,n-1) scanf("%d%d",&x,&y),insert(x,y),insert(y,x);
        ans=0;
        dfs(1);
        printf("%d\n",ans);
    }
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值