1. 程式人生 > >【bzoj3696】化合物 樹形dp

【bzoj3696】化合物 樹形dp

data 個數 蛋疼 一個 題解 max 輸入 logs space

題目描述

首長NOI慘跪,於是去念文化課了。現在,他面對一道化學題。
這題的來源是因為在一個奇怪的學校兩個化競黨在玩一個奇怪的博弈論遊戲。這個遊戲很蛋疼,我相信你們也沒有興趣聽。
由於這個遊戲涉及博弈論,因此化競的同學就要求首長求一個類似SG函數的值。
他們手中有一種非常神奇的化合物,它的分子由N個原子組成(不要在意一個原子可能和及其多個原子成鍵這個細節)。這個分子構成一個樹結構,1號分子為根。 若兩個原子i、j到它們的最近公共祖先的距離分別是Li和Lj,定義它們的Aij值為:
Aij=Li xor Lj
題目要求對於每一個k(k∈N),求出兩兩A值為k的原子對個數。

輸入

第一行一個整數N。

接下來N-1行,每行一個整數p,第i行的整數表示第i個原子的父親為p。

輸出

從K=0開始,第k+1行輸出兩兩A值為K的原子對個數,輸出到第一個不為零的數為止。

樣例輸入

3
1
1

樣例輸出

1
2


題解

樹形dp

第一眼看到這題感覺直接上樹形dp可能會TLE,但是仔細分析復雜度後發現並沒有那麽大。

然後隨便搞了搞就交上去了, 結果不但沒T,而且跑得飛快(同學壓了壓狀態然後怒拿rank1)

設f[i][j]表示以i為根的子樹中深度為j的節點個數。

考慮答案,枚舉x中深度為j的節點和x的兒子s中深度為k的節點,那麽顯然$ans[(j-deep[x])xor(k-deep[x])]+=f[x][j]*f[s][k]$。

然後再更新x。

上界設的緊一點就可以穩切了。

時間復雜度不太會證,大概是$O(nh\log h)$的。

另外本題親測不需要開long long

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
using namespace std;
int head[N] , to[N] , next[N] , cnt , f[510][N] , deep[N] , md[N] , ans[520];
void add(int x , int y)
{
	to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void dfs(int x)
{
	int i , j , k;
	md[x] = deep[x] , f[deep[x]][x] ++ ;
	for(i = head[x] ; i ; i = next[i])
	{
		deep[to[i]] = deep[x] + 1 , dfs(to[i]);
		for(j = deep[x] ; j <= md[x] ; j ++ )
			for(k = deep[to[i]] ; k <= md[to[i]] ; k ++ )
				ans[(j - deep[x]) ^ (k - deep[x])] += f[j][x] * f[k][to[i]];
		for(j = deep[to[i]] ; j <= md[to[i]] ; j ++ )
			f[j][x] += f[j][to[i]];
		md[x] = max(md[x] , md[to[i]]);
	}
}
int main()
{
	int n , i , x;
	scanf("%d" , &n);
	for(i = 2 ; i <= n ; i ++ ) scanf("%d" , &x) , add(x , i);
	dfs(1);
	for(i = 0 ; ans[i] ; i ++ ) printf("%d\n" , ans[i]);
	return 0;
}

【bzoj3696】化合物 樹形dp