BZOJ 4182 Shopping (点分治+树上多重背包)
题目大意:给你一颗树,你有$m$元钱,每个节点都有一种物品,价值为$w$,代价为$c$,有$d$个,如果在$u$和$v$两个城市都购买了至少一个物品,那么$u,v$路径上每个节点也都必须买至少一个物品
单调队列数组开小了调了2h
通过这道题,本蒟蒻终于$get$到了树上带权背包的正确姿势
合并背包的代价是$O(m^{2})$的,非常不友好,而在序列上处理背包时,是不需要合并背包的,所以我们把树拍成$dfs$序
显然,树上背包需要用子节点更新父节点的信息,所以倒序枚举时间戳$i$
设$f[i][j]$表示时间戳为$i$,总代价为$j$时,所有时间戳$>=i$的节点,树上背包能得到的最大价值,令$x$表示时间戳为$i$的节点编号
如果不选节点$x$,那么它子树内的节点都不能选,为了保证$f[i]$是$i$后面所有节点构成的最优解,所以跳过$x$子树的状态,用$f[ed_{x}+1]$更新$f[i]$,$ed_{x}$表示节点$x$的出栈时间
如果选节点$x$,那么可以选$x$的子树内的节点,用$f[i+1]$更新$f[i]$
两者取最优解即可
树上带权$01$背包的时间被我们优化成了$O(nm)$
多重背包可以用单调队列优化,时间一样是$O(nm)$
而上面的$dp$方程仅适用于必须链并经过根节点的情况
所以使用点分治每次选择一个重心作为根跑$DP$
总时间$O(Tnmlogn)$
代码巨丑
1 #include <cmath> 2 #include <vector> 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 #define N1 510 7 #define M1 4010 8 #define ll long long 9 #define inf 233333333 10 using namespace std; 11 12 int gint() 13 { 14 int ret=0,fh=1;char c=getchar(); 15 while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();} 16 while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();} 17 return ret*fh; 18 } 19 int n,K,T; 20 struct Edge{ 21 int to[M1],nxt[M1],head[N1],cte; 22 void ae(int u,int v) 23 {cte++;to[cte]=v,nxt[cte]=head[u],head[u]=cte;} 24 }E; 25 26 int W[N1],C[N1],D[N1]; 27 int st[N1],ed[N1],id[N1],tot; 28 int sz[N1],lim[N1]; 29 int use[N1],mi,G; 30 void gra(int u,int dad,int szfa) 31 { 32 int j,v,ma=szfa; 33 if(szfa>mi) return; 34 for(j=E.head[u];j;j=E.nxt[j]) 35 { 36 v=E.to[j]; if(use[v]||v==dad) continue; 37 ma=max(ma,sz[v]); 38 gra(v,u,szfa+sz[u]-sz[v]); 39 } 40 if(ma<mi) mi=ma,G=u; 41 } 42 void dfs_pre(int u,int dad) 43 { 44 int j,v; sz[u]=0; 45 st[u]=++tot; id[tot]=u; 46 for(j=E.head[u];j;j=E.nxt[j]) 47 { 48 v=E.to[j]; if(use[v]||v==dad) continue; 49 lim[v]=lim[u]-C[u]; 50 dfs_pre(v,u); sz[u]+=sz[v]; 51 } 52 sz[u]++; ed[u]=tot; 53 } 54 int que[M1],hd,tl; 55 int f[N1][M1],ans,de; 56 void calc(int u) 57 { 58 int i,j,k,p,x,w,d,c; 59 memset(f[tot+1],0,sizeof(f[tot+1])); 60 for(i=tot;i;i--) 61 { 62 x=id[i]; c=C[x]; d=D[x]; w=W[x]; 63 memcpy(f[i],f[ed[x]+1],sizeof(f[i])); 64 for(j=0;j<c;j++) 65 { 66 hd=1,tl=0,que[++tl]=0; 67 for(k=1;k*c+j<=lim[x];k++) 68 { 69 while(hd<=tl&&k-que[hd]>d) 70 hd++; 71 f[i][k*c+j]=max(f[i][k*c+j],f[i+1][que[hd]*c+j]+(k-que[hd])*w); 72 while(hd<=tl&&f[i+1][k*c+j]-k*w>=f[i+1][que[tl]*c+j]-que[tl]*w) 73 tl--; 74 que[++tl]=k; 75 } 76 } 77 } 78 for(j=0;j<=K;j++) ans=max(ans,f[1][j]); 79 } 80 void main_dfs(int u) 81 { 82 int j,v; 83 use[u]=1; tot=0,lim[u]=K; 84 dfs_pre(u,-1); 85 calc(u); 86 for(j=E.head[u];j;j=E.nxt[j]) 87 { 88 v=E.to[j]; if(use[v]) continue; 89 mi=inf,G=0,gra(v,u,0); 90 main_dfs(G); 91 } 92 } 93 void MAIN() 94 { 95 dfs_pre(1,-1); 96 mi=inf,G=0,gra(1,-1,0); 97 main_dfs(G); 98 } 99 void init() 100 { 101 tot=0,E.cte=0,ans=0; 102 memset(use,0,sizeof(use)); 103 memset(E.head,0,sizeof(E.head)); 104 } 105 106 int main() 107 { 108 freopen("t2.in","r",stdin); 109 scanf("%d",&T); 110 while(T--) 111 { 112 scanf("%d%d",&n,&K); 113 int i,x,y,z; init(); 114 for(i=1;i<=n;i++) W[i]=gint(); 115 for(i=1;i<=n;i++) C[i]=gint(); 116 for(i=1;i<=n;i++) D[i]=gint(); 117 for(i=1;i<n;i++) 118 { 119 x=gint(), y=gint(); 120 E.ae(x,y),E.ae(y,x); 121 } 122 MAIN(); 123 printf("%d\n",ans); 124 } 125 return 0; 126 }