1. 程式人生 > 其它 >#樹形依賴揹包,點分治#BZOJ 4182 Shopping

#樹形依賴揹包,點分治#BZOJ 4182 Shopping

題目

給定一棵大小為 \(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;
}