树异或价值

题目链接

  • 对LCA的转化、二进制拆位都想到了,每个子树内0、1数量差不超过1的结论也猜出来了,也知道要用树形DP做,只是没能推出这个DP式子
  • 不过这个DP式子确实难推啊,听说有队伍朴素DP然后分治NTT优化……
  • 避免繁琐的分类讨论,简化问题,不妨强制赋值子树根节点为0
  • 尝试理解题解中的DP式子
  • 子树大小为偶数的情况很好理解
  • 对于子树大小为奇数的情况:首先,强制赋值a[x]=0得到的DP值,通过全部取反,也可以认为是方案中0多或0少的方案数,以下强制1多
  • 当大小为奇数的子树有奇数个时,取出s[1]/2个子树反转
  • 当大小为奇数的子树有偶数个时,取出s[1]/2个子树反转,得到0比1多1的情况;取出s[1]/2-1个子树反转,得到1比0多1的情况
  • 也就是说,不是尝试依次让它满足题意,而是直接构造
  • HDOJ评测时将数组命名为size会导致编译错误
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int mod=998244353;
vector<int>a[200005];
int w[200005],sz[200005],s[200005][2];
long long jc[200005],jcinv[200005],f[200005];
int n,k;
int power(int n,int p)
{
	if(p==0)
	{
		return 1;
	}
	long long tmp=power(n,p/2);
	if(p%2==1)
	{
		return tmp*tmp%mod*n%mod;
	}
	return tmp*tmp%mod;
}
void pre()
{
	jc[0]=1;
	for(int i=1;i<=200000;i++)
	{
		jc[i]=jc[i-1]*i%mod;
	}
	jcinv[200000]=power(jc[200000],998244351);
	for(int i=200000-1;i>=0;i--)
	{
		jcinv[i]=jcinv[i+1]*(i+1)%mod;
	}
}
int c(int n,int m)
{
	return jc[n]*jcinv[m]%mod*jcinv[n-m]%mod;
}
void dfs(int n1)
{
	sz[n1]=1;
	for(int i=0;i<a[n1].size();i++)
	{
		dfs(a[n1][i]);
		sz[n1]+=sz[a[n1][i]];
	}
}
void dp(int n1)
{
	f[n1]=1;
	s[n1][0]=s[n1][1]=0;
	for(int i=0;i<a[n1].size();i++)
	{
		s[n1][sz[a[n1][i]]%2]++;
		dp(a[n1][i]);
		f[n1]=f[n1]*f[a[n1][i]]%mod;
	}
	f[n1]=f[n1]*power(2,s[n1][0])%mod;
	f[n1]=f[n1]*((c(s[n1][1],s[n1][1]/2)+(s[n1][1]%2==0)*c(s[n1][1],s[n1][1]/2-1))%mod)%mod;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	pre();
	int T;
	cin>>T;
	while(T--)
	{
		cin>>n>>k;
		for(int i=1;i<=n;i++)
		{
			a[i].clear();
		}
		for(int i=2;i<=n;i++)
		{
			int p;
			cin>>p;
			a[p].push_back(i);
		}
		dfs(1);
		dp(1);
		cout<<power(2*f[1]%mod,k)<<endl;
	}
	return 0;
}
/*
1
9 3
1 1 2 2 2 3 7 7
*/
posted @ 2024-08-21 15:33  D06  阅读(21)  评论(0编辑  收藏  举报