1. 程式人生 > >[bzoj4543][bzoj3522][DP][長鏈剖分]Hotel&Hotel加強版

[bzoj4543][bzoj3522][DP][長鏈剖分]Hotel&Hotel加強版

Description

有一個樹形結構的賓館,n個房間,n-1條無向邊,每條邊的長度相同,任意兩個房間可以相互到達。吉麗要給他的三個妹子各開(一個)房(間)。三個妹子住的房間要互不相同(否則要打起來了),為了讓吉麗滿意,你需要讓三個房間兩兩距離相同。
有多少種方案能讓吉麗滿意?

Input

第一行一個數n。 接下來n-1行,每行兩個數x,y,表示x和y之間有一條邊相連。

Output

讓吉麗滿意的方案數。

Sample Input

7

1 2

5 7

2 5

2 3

5 6

4 5

Sample Output

5

HINT

【樣例解釋】

{1,3,5},{2,4,6},{2,4,7},{2,6,7},{4,6,7}

【資料範圍】

n≤5000

題解

首先有個性質
這三個點的路徑會交於一點,三個點到這個點的距離都相等且位於同一棵子樹
那麼 N 2

N^2 的就可以直接做了…
這個 O ( N ) O(N) 的有點神仙…
設一個dp狀態
f
[ i ] [ j ] f[i][j]
表示 i i 點子樹中有多少距離為 j j 的點
g [ i ] [ j ] g[i][j] 表示 i i 點子樹中,有多少對,在 i i 的子樹外面找一個距離為 j j 的點就能構成一個合法三元組的數量
每掃到一棵子樹 y y
可以這樣累加答案
a n s + = f [ x ] [ j ] s u m [ y ] [ j + 1 ] + s u m [ x ] [ j ] f [ y ] [ j 1 ] ans+=\sum f[x][j]*sum[y][j+1]+sum[x][j]*f[y][j-1]
然後更新這樣更新
g [ x ] [ i ] + = f [ x ] [ i ] f [ y ] [ i 1 ] g[x][i]+=f[x][i]*f[y][i-1]
f [ x ] [ i ] + = f [ y ] [ i 1 ] f[x][i]+=f[y][i-1]
g [ x ] [ i ] + = g [ y ] [ i + 1 ] g[x][i]+=g[y][i+1]
注意到這樣是 m a x d e p [ i ] \sum maxdep[i]
考慮如何優化
我們發現,每個點的第一次轉移其實就是找一個兒子,讓他的 g g 陣列左移一位, f f 陣列右移一位
指標移動即可
這樣可以節省 m a x d e p [ s o n [ i ] ] \sum maxdep[son[i]] 的複雜度
長鏈剖分,每個點選擇它的重兒子做更新
其他兒子暴力更新
單考慮重鏈上的轉移,一次是 O ( 1 ) O(1) 的,所以總複雜度不會超過 O ( N ) O(N)
再考慮輕邊,每條重鏈只會在頭的位置被計算完整個重鏈的長度
顯然重鏈沒有重疊
所以輕邊的轉移總複雜度也是 O ( N ) O(N)
於是就是 O ( N ) O(N) 的優秀複雜度了…
空間的話…學習了一下別人的開法畢竟不會指標陣列這個東西

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#define LL long long
#define mp(x,y) make_pair(x,y)
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int stack[20];
inline void write(LL x)
{
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(LL x){write(x);putchar('\n');}
struct node{int y,next;}a[210000];int len,last[110000];
void ins(int x,int y){len++;a[len].y=y;a[len].next=last[x];last[x]=len;}
LL spa[100005*10];
LL *f[100005],*g[100005],*now=spa+100005,ans;
int dep[100005],son[100005];
void init(int x,int fa)
{
	for(int k=last[x];k;k=a[k].next)
	{
		int y=a[k].y;
		if(y!=fa)
		{
			init(y,x);
			if(dep[y]>dep[son[x]])son[x]=y;
		}
	}
	dep[x]=dep[son[x]]+1;
}
void newnode(int x)
{
	f[x]=now;now=now+2*dep[x]+1;
	g[x]=now;now=now+2*dep[x]+1;
}
void dp(int x,int fa)
{
	f[x][0]=1;
	if(son[x])
	{
		f[son[x]]=f[x]+1;g[son[x]]=g[x]-1;
		dp(son[x],x);
		ans+=g[son[x]][1];
	}
	for(int k=last[x];k;k=a[k].next)
	{
		int y=a[k].y;
		if(y!=fa&&y!=son[x])
		{
			newnode(y);dp(y,x);
			for(int j=dep[y];j>=0;j--)
			{
				if(j)ans=ans+g[y][j]*f[x][j-1];
				ans=ans+g[x][j+1]*f[y][j];
				g[x][j+1]+=f[x][j+1]*f[y][j];
			}
			for(int j=dep[y];j>=0;j--)
			{
				if(j)g[x][j-1]+=g[y][j];
				f[x][j+1]+=f[y][j];
			}
		}
	}
}
int n;
int main()
{
	n=read();
	for(int i=1;i<n;i++)
	{
		int x=read(),y=read();
		ins(x,y);ins(y,x);
	}
	init(1,0);
	newnode(1);
	dp(1,0);
	pr2(ans);
	return 0;
}