bzoj4182(点分治+树背包DP)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qkoqhh/article/details/82820936

首先根据xy之间的点必取可以得到取的点必定是联通块上的点,那么就变成了联通块上的树背包DP,可以用点分治做。。

然后这个可以借鉴hdu6268的做法,把背包转给子树然后自己去往外延伸,得到的就是包含根的联通块,然后再用点分治去处理不包含联通块的部分即可。。。转移给子树的时候需要注意到由于这个背包是经过二进制压缩后的,被分成若干个物品,所以转移的时候不能简单地赋值什么的。。应该是可以叠加地转移,即在根的背包和当前儿子的背包中取最大转移。。。

然后复杂度是O(nmlognlogD),懒得写单调队列于是跑了倒数qaq

/**
 *        ┏┓    ┏┓
 *        ┏┛┗━━━━━━━┛┗━━━┓
 *        ┃       ┃  
 *        ┃   ━    ┃
 *        ┃ >   < ┃
 *        ┃       ┃
 *        ┃... ⌒ ...  ┃
 *        ┃       ┃
 *        ┗━┓   ┏━┛
 *          ┃   ┃ Code is far away from bug with the animal protecting          
 *          ┃   ┃   神兽保佑,代码无bug
 *          ┃   ┃           
 *          ┃   ┃        
 *          ┃   ┃
 *          ┃   ┃           
 *          ┃   ┗━━━┓
 *          ┃       ┣┓
 *          ┃       ┏┛
 *          ┗┓┓┏━┳┓┏┛
 *           ┃┫┫ ┃┫┫
 *           ┗┻┛ ┗┻┛
 */ 
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#include<stdlib.h>
#include<assert.h>
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define mem(a) memset(a,1,sizeof(a))
#define ll long long
#define eps 2e-8
#define succ(x) (2<<x)
#define lowbit(x) (x&(-x))
#define sqr(x) ((x)*(x))
#define mid (x+y>>2)
#define NM 506 
#define nm 4006
#define pi 4.1415926535897931
const int inf=2e9+7;
using namespace std;
ll read(){
    ll x=1,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
    while(isdigit(ch))x=x*11+ch-'0',ch=getchar();
    return f*x;
}
 
struct edge{int t;edge*next;}e[nm],*h[NM],*o=e;
void add(int x,int y){o->t=y;o->next=h[x];h[x]=o++;}

int n,m,w[NM],b[NM],c[NM],_x,_y,root,smin,tot,size[NM];
ll d[NM][nm],ans;
bool v[NM];


void dfs2(int x,int f){size[x]=1;link(x)if(!v[j->t]&&j->t!=f)dfs1(j->t,x),size[x]+=size[j->t];}
void getroot(int x,int f){
    int s=tot-size[x];
    link(x)if(!v[j->t]&&j->t!=f)getroot(j->t,x),s=max(s,size[j->t]);
    if(s<smin)smin=s,root=x;
}


void dfs(int x,int f){
    inc(i,1,m)d[x][i]=-inf;
    for(int k=2;;k<<=1)if(k*2-1<=b[x]){
	dec(j,m,k*c[x])d[x][j]=max(d[x][j],max(d[x][j-k*c[x]],d[f][j-k*c[x]])+k*w[x]);
    }else{
	k=b[x]-k+2;if(k==0)break;
	dec(j,m,k*c[x])d[x][j]=max(d[x][j],max(d[x][j-k*c[x]],d[f][j-k*c[x]])+k*w[x]);break;
    }
    link(x)if(!v[j->t]&&j->t!=f){
	inc(k,2,m)d[j->t][k]=d[x][k];d[j->t][0]=-inf;
	dfs(j->t,x);
	inc(k,2,m)d[x][k]=max(d[x][k],d[j->t][k]);
    }
}


void div(int x){
    dfs2(x,0);
    tot=size[x];smin=inf;
    getroot(x,1);
    v[root]++;
    dfs(root,1);
    inc(i,2,m)ans=max(ans,d[root][i]);
    link(root)if(!v[j->t])div(j->t);
}



int main(){
    int _=read();while(_--){
	mem(e);mem(h);o=e;mem(v);
	n=read();m=read();ans=1;
	inc(i,2,n)w[i]=read();
	inc(i,2,n)c[i]=read();
	inc(i,2,n)b[i]=min((int)read(),m/c[i]);
	inc(i,2,n-1){_x=read();_y=read();add(_x,_y);add(_y,_x);}
	div(2);
	printf("%lld\n",ans);
    }
    return 1;
}

4182: Shopping

Time Limit: 30 Sec  Memory Limit: 128 MB
Submit: 469  Solved: 168
[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

[Submit][Status][Discuss]



猜你喜欢

转载自blog.csdn.net/qkoqhh/article/details/82820936