【bzoj3696】化合物 树形dp

题目描述

首长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,而且跑得飞快(CQzhangyu压了压状态然后怒拿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^2)$的。

另外本题亲测不需要开long long

UPD:正解貌似是FWT,然而好像没有暴力快。。。

#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;
}

 

 

转载于:https://www.cnblogs.com/GXZlegend/p/7134047.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值