#樹形依賴揹包,點分治#BZOJ 4182 Shopping
阿新 • • 發佈:2022-03-20
題目
給定一棵大小為 \(n\) 的樹,每個點代表一種物品,其具有體積、價值和數量的屬性,
現在選擇一個連通塊,使得裡面所有點都被選中且體積不超過 \(m\),問最大價值。
\(n\leq 500,m\leq 4000\)
分析
樹形揹包比較難維護,考慮用dfs序拍平到序列上,並且多重揹包直接二進位制拆分。
設 \(dp[i][j]\) 表示dfs序為 \(i\),且選擇體積為 \(j\) 時能獲得的最大價值。
如果不選這個點,那麼 \(dp[i][j]=dp[rfn[i]][j]\),\(rfn\) 表示這個點的下一個兄弟的dfs序
如果選擇這個點,那麼 \(dp[i][j]=\max\{dp[i+1][j-w]+c\}\)
但有一個問題就是這樣會變成01揹包,考慮先用上式更新一次(強制必選一個),再用 \(dp[i][j-w]\) 更新。
就是在二進位制拆分時先拆一個再正常拆,發現這樣根節點強制必選,那麼跑點分治,所有連通塊都能被以當前根節點的情況所表示。
可以通過二進位制拆分的個數來決定點的大小求帶權重心,這樣時間複雜度為 \(O(Tnm\log n\log m)\)
程式碼
#include <cstdio> #include <cctype> using namespace std; const int N=511; struct node{int y,next;}e[N<<1]; struct rec{int w,c;}a[N<<3]; int siz[N],big[N],as[N],L[N],R[N],w[N],c[N],ans,root,tot,v[N],dfn[N],nfd[N],rfn[N],et=1,n,m,k,dp[N][N<<3]; int iut(){ int ans=0; char c=getchar(); while (!isdigit(c)) c=getchar(); while (isdigit(c)) ans=ans*10+c-48,c=getchar(); return ans; } void print(int ans){ if (ans>9) print(ans/10); putchar(ans%10+48); } void Max(int &x,int y){x=x>y?x:y;} void dfs(int x,int fa){ siz[x]=R[x]-L[x]+1,big[x]=0; for (int i=as[x];i;i=e[i].next) if (e[i].y!=fa&&!v[e[i].y]){ dfs(e[i].y,x),siz[x]+=siz[e[i].y]; Max(big[x],siz[e[i].y]); } Max(big[x],big[0]-siz[x]); if (big[x]<=big[root]) root=x; } void calc(int x,int fa){ dfn[x]=++tot,nfd[tot]=x; for (int i=as[x];i;i=e[i].next) if (e[i].y!=fa&&!v[e[i].y]) calc(e[i].y,x); rfn[x]=tot+1; } void Dp(int x){ v[x]=1,tot=0,calc(x,0); for (int i=0;i<=k;++i) dp[tot+1][i]=0; for (int i=tot;i;--i){ int x=nfd[i]; for (int j=0;j<=k;++j) dp[i][j]=dp[rfn[x]][j]; for (int j=k;j>=w[x];--j) Max(dp[i][j],dp[i+1][j-w[x]]+c[x]); for (int o=R[x];o>L[x];--o) for (int j=k;j>=a[o].w;--j) Max(dp[i][j],dp[i][j-a[o].w]+a[o].c); } Max(ans,dp[1][k]); for (int i=as[x];i;i=e[i].next) if (!v[e[i].y]){ big[0]=siz[e[i].y]; dfs(e[i].y,root=0),Dp(root); } } int main(){ for (int T=iut();T;--T){ n=iut(),k=iut(),ans=m=0,et=1; for (int i=1;i<=n;++i) c[i]=iut(); for (int i=1;i<=n;++i) w[i]=iut(); for (int i=1;i<=n;++i){ int x=iut(); L[i]=R[i-1]+1; a[++m]=(rec){w[i],c[i]},--x; for (int t=1;x>=t;x-=t,t<<=1) a[++m]=(rec){w[i]*t,c[i]*t}; if (x) a[++m]=(rec){w[i]*x,c[i]*x}; R[i]=m; } for (int i=1;i<n;++i){ int x=iut(),y=iut(); e[++et]=(node){y,as[x]},as[x]=et; e[++et]=(node){x,as[y]},as[y]=et; } big[0]=m,dfs(1,root=0),Dp(root); print(ans),putchar(10); for (int i=1;i<=n;++i) v[i]=as[i]=0; } return 0; }