题目链接
- 对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
*/