1. 程式人生 > >BZOJ 4182 Shopping (樹分治+樹上多重揹包)

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 }