1. 程式人生 > >【長鏈剖分】【DP】BZOJ4543[POI2014]Hotel加強版

【長鏈剖分】【DP】BZOJ4543[POI2014]Hotel加強版

題意:

給出一棵 n 個點的無根樹,請在這棵樹上選三個互不相同的節點,使得這個三個
節點兩兩之間距離相等,輸出方案數即可。


分析:

定義
f ( x , i ) f(x,i)

表示在以x為根的子樹中,與x距離為i的節點數
g ( x , i ) g(x,i) 表示在以x為根的子樹中選擇了兩個節點,最後一個點需滿足與x的距離為i的方案數。
所以 f
( x , i ) = u f (
u , i 1 ) f(x,i)=\sum_u f(u,i-1)

g ( x , i ) = ( u g ( u , i + 1 ) ) + ( u , v f ( u , i ) f ( v , i ) ) g(x,i)=(\sum_u g(u,i+1))+(\sum_{u,v}f(u,i)*f(v,i))

可以通過繼承其最大兒子的DP值,使其最大兒子的轉移速度為O(1),再O(n)暴力列舉每個輕兒子的最大深度。

繼承的具體操作,可以通過預開記憶體的方式實現。
(即建立一個記憶體池,然後f(x)從 i i 位置開始, f ( s o n x ) f(son_x) 就從 i + 1 i+1 位置開始,g(x)從i位置開始, g ( s o n x ) g(son_x) 就從i-1位置開始,詳見程式碼中兩個dfs)

總的複雜度為 O ( ) = O ( n ) O(\sum 重鏈長度)=O(n)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 100010
using namespace std;
typedef long long ll;
vector<int> a[MAXN];
ll Gpool[MAXN*2];
ll Fpool[MAXN];
ll *f[MAXN],*g[MAXN],*fcnt=Fpool,*gcnt=Gpool;
int maxl[MAXN],son[MAXN];
void prepare(int x,int fa=0){
	for(int i=0;i<int(a[x].size());i++){
		int u=a[x][i];
		if(u==fa)
			continue;
		prepare(u,x);
		maxl[x]=max(maxl[x],maxl[u]+1);
	}
	for(int i=0;i<int(a[x].size());i++){
		int u=a[x][i];
		if(u==fa)
			continue;
		if(son[x]==0||maxl[u]>maxl[son[x]])
			son[x]=u;
	}
}
void dfsf(int x,int fa=0){
	f[x]=++fcnt;
	if(son[x])
		dfsf(son[x],x);
	for(int i=0;i<int(a[x].size());i++){
		int u=a[x][i];
		if(u==fa||u==son[x])
			continue;
		dfsf(u,x);
	}
}
void dfsg(int x,int fa=0){
	for(int i=0;i<int(a[x].size());i++){
		int u=a[x][i];
		if(u==fa||u==son[x])
			continue;
		dfsg(u,x);
		gcnt+=maxl[u];
	}
	if(son[x])
		dfsg(son[x],x);
	g[x]=++gcnt;
}
ll ans;
void dp(int x,int fa=0){
	f[x][0]=1;
	if(son[x]==0)
		return ;
	dp(son[x],x);
	ans+=g[x][0];
	for(int i=0;i<int(a[x].size());i++){
		int u=a[x][i];
		if(u==fa||u==son[x])
			continue;
		dp(u,x);	
		for(int i=1;i<=maxl[u]+1;i++)
			ans+=g[x][i]*f[u][i-1];
		for(int i=0;i<=maxl[u]-1;i++)
			ans+=f[x][i]*g[u][i+1];
		for(int i=0;i<=maxl[u]-1;i++)
			g[x][i]+=g[u][i+1];
		for(int i=1;i<=maxl[u]+1;i++)
			g[x][i]+=(f[u][i-1]*f[x][i]);
		for(int i=1;i<=maxl[u]+1;i++)
			f[x][i]+=f[u][i-1];
	}
//	PF("%d %d(%lld):\n",x,maxl[x],ans);
//	for(int i=0;i<=maxl[x];i++)
//		PF("%d:[%lld %lld]\n",i,f[x][i],g[x][i]);
//	PF("-------------------\n");
}
int n,u,v;
void init(){
	fcnt=Fpool;
	gcnt=Gpool;
	memset(Fpool,0,sizeof Fpool);
	memset(Gpool,0,sizeof Gpool);
	memset(maxl,0,sizeof maxl);
	memset(son,0,sizeof son);
	for(int i=1;i<=n;i++)
		a[i].clear();
	ans=0;	
}
int main(){
//	freopen("three1-3.in","r",stdin);
	while(SF("%d",&n)!=EOF){
		if(n==0)
			break;
		init();
		for(int i=1;i<n;i++){
			SF("%d%d",&u,&v);
			a[u].push_back(v);
			a[v].push_back(u);	
		}
		prepare(1);
		dfsf(1);
		dfsg(1);
		dp(1);
		PF("%lld\n",ans);
	}
}