首先根据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