【BZOJ4182: Shopping】静态点分治+依赖树形多重背包

我可能原本对于多重背包的二进制分组有所误解orz orz orz  

4182: Shopping

Time Limit: 30 Sec  Memory Limit: 128 MB Submit: 500  Solved: 177 [Submit][Status][Discuss]

Description

马上就是小苗的生日了,为了给小苗准备礼物,小葱兴冲冲地来到了商店街。商店街有n个商店,并且它们之间的道路构成了一颗树的形状。
第i个商店只卖第i种物品,小苗对于这种物品的喜爱度是wi,物品的价格为ci,物品的库存是di。但是商店街有一项奇怪的规定:如果在商店u,v买了东西,并且有一个商店w在u到v的路径上,那么必须要在商店w买东西。小葱身上有m元钱,他想要尽量让小苗开心,所以他希望最大化小苗对买
到物品的喜爱度之和。这种小问题对于小葱来说当然不在话下,但是他的身边没有电脑,于是他打电话给同为OI选手的你,你能帮帮他吗?

Input

输入第一行一个正整数T,表示测试数据组数。
对于每组数据,
第一行两个正整数n;m;
第二行n个非负整数w1,w2...wn;
第三行n个正整数c1,c2...cn;
第四行n个正整数d1,d2...dn;
接下来n-1行每行两个正整数u;v表示u和v之间有一条道路

Output

输出共T 行,每行一个整数,表示最大的喜爱度之和。

Sample Input

1 3 2 1 2 3 1 1 1 1 2 1 1 2 1 3

Sample Output

4

HINT

 N<=500,M<=4000,T<=5,Wi<=4000,Di<=100

Source

题意大概就是,找出一个最大的当根的点的最大树形依赖多重背包的。 于是乎对于换根考虑,我们静态点分治解决之。对于树形背包有一个dfs序的背包方式: F[i][j] 考虑完dfs序为i,i+1,i+2.....n的所有点后,背包容量为j的最大贡献。转移选和不选,选的话就考虑选不选子树否则就跳过这一段dfs序。 如果对于普通0/1树形依赖背包的转移方程f[i][j] = max: f[i+1][j-1]+V[i] , f[i+siz[i]][j] 而对于本题还有物品限制。我们利用二进制分组一下,把物品拆分,如7我们拆成1,2,4,,如9我们拆成1,2,4,2。也就是前面我们尽可能用2的次幂去填,不够了就直接留下最后那一段。可以想到这样是可以完全将所有物品的个数的情况考虑到的。 时间复杂度(O(Tnm*log2 di * log2 n) code:
#include<stdio.h>
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn = 505;
const int maxm = 4005;
int n,m; bool vis[maxn];
int w[maxn],c[maxn],d[maxn],sz[maxn];//jiage chengben shuliang
int en[maxn*2],nt[maxn*2],la[maxn],owo;
void adg(int x,int y) {
    en[++owo]=y; nt[owo]=la[x]; la[x]=owo;
}
using namespace std;
int RT,rtsz,sm,ANS;
int f[maxn][maxm];
void frt(int x,int ba) {
    sz[x]=1; int ss=0;
    for(int it=la[x];it;it=nt[it]) {
        int y = en[it];
        if(y==ba||vis[y]) continue;
        frt(y,x);
        sz[x]+=sz[y]; ss=max(ss,sz[y]);
    } ss=max(sm-sz[x],ss);
    if(ss<rtsz) rtsz=ss,RT=x;
}
int dy[maxn],dfx;
void dfs(int x,int ba) {
    dy[++dfx] = x; sz[x]=1;
    for(int it=la[x];it;it=nt[it]) {
        int y = en[it];
        if(y==ba||vis[y]) continue;
        dfs(y,x);
        sz[x] += sz[y];
    }
}
void calc(int x) {
    dfx=0; dfs(x,0);
    for(int i=dfx;i>=1;i--) {
        int k = dy[i];
        for(int j=1;j<=m;j++) f[i][j] = f[i+sz[k]][j];
            int od = 1; int oz = d[k];
            for(;od<=oz;od<<=1) {
                oz-=od;
                for(int j=m;j>=od*c[k];j--) {
                    int cost = od*c[k]; int vv = od*w[k];
                    f[i][j] = max(f[i][j], max(f[i][j-cost]+vv,f[i+1][j-cost]+vv));
            }
        }
        if(oz>0) {
            for(int j=m;j>=oz*c[k];j--) {
            int cost = oz*c[k]; int vv = oz*w[k];
            f[i][j] = max(f[i][j], max(f[i][j-cost]+vv,f[i+1][j-cost]+vv));
            
        }
        }
    }
    ANS = max(ANS,f[1][m]);
    for(int i=1;i<=dfx;i++) {
        for(int j=0;j<=m;j++) f[i][j]=0;
    }
}
void work(int x) {
    calc(x); vis[x] = 1;
    for(int it=la[x];it;it=nt[it]) {
        int y = en[it];
        if(vis[y]) continue;
        sm = sz[y]; rtsz = 0x3f3f3f3f; frt(y,x);
        work(RT);
    }
}
void solve() {
    scanf("%d%d",&n,&m); 
    owo = ANS = 0; 
    for(int i=1;i<=n;i++) la[i] = vis[i] = 0;
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    for(int i=1;i<=n;i++) scanf("%d",&c[i]);
    for(int i=1;i<=n;i++) scanf("%d",&d[i]);
    for(int i=1;i<n;i++) {
        int x,y; scanf("%d%d",&x,&y);
        adg(x,y); adg(y,x);
    }
    sm=n; rtsz=0x3f3f3f3f; frt(1,0);
    work(RT);
    printf("%d\n",ANS);
}
int main() {
    int T;
    scanf("%d",&T);
    while(T--) solve();
}
   
posted @ 2018-11-29 14:35  Newuser233  阅读(8)  评论(0)    收藏  举报