[Bzoj3696]化合物【暴力+树形Dp】

Online JudgeBzoj3696

Label:暴力,树形Dp

题目描述

首长NOI惨跪,于是去念文化课了。现在,他面对一道化学题。
这题的来源是因为在一个奇怪的学校两个化竞党在玩一个奇怪的博弈论游戏。这个游戏很蛋疼,我相信你们也没有兴趣听。由于这个游戏涉及博弈论,因此化竞的同学就要求首长求一个类似SG函数的值。

他们手中有一种非常神奇的化合物,它的分子由N个原子组成(不要在意一个原子可能和及其多个原子成键这个细节)。这个分子构成一个树结构,1号分子为根。 若两个原子i、j到它们的最近公共祖先的距离分别是Li和Lj,定义它们的(Aij)值为:(Aij=Li) Xor (Lj)
题目要求对于每一个k(k∈N),求出两两A值为k的原子对个数。

输入

第一行一个整数N。
接下来N-1行,每行一个整数p,第新亍的整数表示第i个原子的父亲为p。

输出

从K=0开始,第k+1行输出两两A值为K的原子对个数,输出到第一个不为零的数为止。

样例

Input

3
1
1

Output

1
2

题解

对于这个数据范围,(O(NK))的时间复杂度完全没有问题,随便暴力怎么搞都行。

定义状态(dp[i][j]),表示以i为根的子树中,离i距离为j的节点有多少个。

递归到x时先用儿子子树的(dp)值更新答案,然后再将儿子子树的(dp)值累加到x的(dp)值这里。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,dp[N][550],madep[N],ans[550];
struct edge{
	int to,nxt;
}e[2*N];
int cnt,head[N];
inline void link(int u,int v){
	e[++cnt].to=v,e[cnt].nxt=head[u];
	head[u]=cnt;
}
void dfs(int x){
	dp[x][0]=1;
	for(int i=head[x];i;i=e[i].nxt){
		int y=e[i].to;
		dfs(y);
		for(int j=0;j<=madep[x];j++)
			for(int k=0;k<=madep[y];k++)ans[j^(k+1)]+=dp[x][j]*dp[y][k];
		madep[x]=max(madep[x],madep[y]+1);
		for(int j=0;j<=madep[y];j++)dp[x][j+1]+=dp[y][j];
	}
}
int main(){
	scanf("%d",&n);
	for(register int i=2,u;i<=n;i++){
		scanf("%d",&u);
		link(u,i);
	}
	dfs(1);
	for(register int i=0;ans[i];i++)printf("%d
",ans[i]);
}
原文地址:https://www.cnblogs.com/Tieechal/p/11524391.html