首页 > 代码库 > 【BZOJ3696】化合物 树形DP+暴力

【BZOJ3696】化合物 树形DP+暴力

【BZOJ3696】化合物

Description

    首长NOI惨跪,于是去念文化课了。现在,他面对一道化学题。
    这题的来源是因为在一个奇怪的学校两个化竞党在玩一个奇怪的博弈论游戏。这个游戏很蛋疼,我相信你们也没有兴趣听。
    由于这个游戏涉及博弈论,因此化竞的同学就要求首长求一个类似SG函数的值。
    他们手中有一种非常神奇的化合物,它的分子由N个原子组成(不要在意一个原子可能和及其多个原子成键这个细节)。这个分子构成一个树结构,1号分子为根。    若两个原子i、j到它们的最近公共祖先的距离分别是Li和Lj,定义它们的Aij值为:
Aij=Li  xor Lj
题目要求对于每一个k(k∈N),求出两两A值为k的原子对个数。

Input

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

Output

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

Sample Input

3
1
1

Sample Output

1
2

HINT

【数据规模与约定】
用h表示树结构分子的最大深度。
 N<=10^5,H<=500

题解:第一直觉是某种树形DP,但是想不出来,看到H很小,感觉暴力可过,发现正解真的是暴力~

对于每一层,我们用f[i]表示当前子树中深度为i的点的个数,然后我们用下一层的f‘[j]和当前的f[i]来暴力更新答案ans[i^j],然后用f‘[j]来更新f[i]就行了。

注意要输出到第一个为0的数,而不是输出所有不为0的数。

随手写了一发rank1了什么鬼~可能因为数组开的比较小吧?

技术分享

#include <cstdio>#include <cstring>#include <iostream>using namespace std;const int maxn=100010;int n,mx,cnt;int fa[maxn],dep[maxn],md[maxn],f[520][520],to[maxn],head[maxn],next[maxn],ans[520];int rd(){	int ret=0,f=1;	char gc=getchar();	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)f=-f;	gc=getchar();}	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();	return ret*f;}void add(int a,int b){	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;}void dfs(int x){	md[x]=dep[x],f[dep[x]][dep[x]]=1;	int i,j,k;	for(i=head[x];i!=-1;i=next[i])	{		dep[to[i]]=dep[x]+1,dfs(to[i]);		for(j=dep[x];j<=md[x];j++)			for(k=dep[x];k<=md[to[i]];k++)				ans[(j-dep[x])^(k-dep[x])]+=f[dep[x]][j]*f[dep[x]+1][k];		for(j=dep[x];j<=md[to[i]];j++)			f[dep[x]][j]+=f[dep[x]+1][j],f[dep[x]+1][j]=0;		md[x]=max(md[x],md[to[i]]);	}}int main(){	n=rd();	int i;	memset(head,-1,sizeof(head));	for(i=2;i<=n;i++)	fa[i]=rd(),add(fa[i],i);	dep[1]=2,dfs(1);	for(i=0;i<512;i++)	{		if(!ans[i])	return 0;		printf("%d\n",ans[i]);	}}

【BZOJ3696】化合物 树形DP+暴力