题目大意
这道题从G题(Operating on a Graph )延伸而来,附上G题大意:
给一个 n 个点的 Graph,第 i 个点一刚开始是第 I 种颜色,接着有 k 次操作,第 i 次操作有个参数 oi 代表颜色 oi 会侵略所有和自己相邻的颜色,于是所有和 oi 相邻的颜色全都变成 oi (若已没有颜色oi 已被侵略,则该次操作无效),求最终每个点的颜色。
给出一棵具有n个顶点的树。设p是从0到n-1的排列,定义函数f(p):p是输入运算符序列,f(p)是满足条件的操作数:执行第i个操作时,至少有一个顶点属于o[i]组。
令S为从0到n-1的所有可能排列的集合。 请计算
官方题解的解释:
给一个n个点的树定义一个排列的花费为,把该序列当作G题的 a 序列,有多少次侵略行为中树上至少有一个是第oi种颜色。计算所有0~n-1的排列的花费总和 mod 998244353。
解题思路
我们把每个点分为两类:好点在一个排列中会对答案有贡献1,坏点没有贡献。
可以分析得出,好点和坏点有一些性质,满足这些的组合就是合法的:
不存在两个相邻好点,每个坏点至少和一个比他大的好点相邻。
这样就可以用一个树形dp来解决了。
这里用dp1储存组合个数,dp2储存所有组合的花费和。存储方法:
dp1/dp2 [子树树根编号] [子树里有多少点比树根大] [树根是好点 / 坏点但尚未有比它大的好点相邻 / 坏点已有比它大的好点相邻]
列出转移式中要用到连续和的技巧。当一棵子树接在另一棵子树下時,有
[ 树根是好点,坏点但尚未有比它大的好点相邻,坏点已有比它大的好点相邻 ]
这三种情况,又分别有
[ 树根是好点,坏点但尚未有比它大的好点相邻,坏点已有比它大的好点相邻 ]
三种情况,乘起来共九种,要全部考虑。
AC代码
#include<bits/stdc++.h>
using namespace std;
const long long mod=998244353;
vector<int> v[2010];
int a[2010],b[2010][2010],dp1[2010][3][2010],dp2[2010][3][2010],t1[3][2010],t2[3][2010],n;
int get(int x[],int l,int r)
{
if(l>r) return 0;
if(l==0) return x[r];
int t=x[r]-x[l-1];
t+=mod*(t<0);
return t;
}
void sum(int x,int y,int t,int u,int v,int e,int f,int l,int r)
{
t1[t][e+f]=(t1[t][e+f]+1ll*dp1[x][u][e]*get(dp1[y][v],l,r)%mod*b[e+f][e]%mod*b[(a[x]-e-1)+(a[y]-f)][a[y]-f])%mod;
t2[t][e+f]=(t2[t][e+f]+1ll*dp2[x][u][e]*get(dp1[y][v],l,r)%mod*b[e+f][e]%mod*b[(a[x]-e-1)+(a[y]-f)][a[y]-f])%mod;
t2[t][e+f]=(t2[t][e+f]+1ll*dp1[x][u][e]*get(dp2[y][v],l,r)%mod*b[e+f][e]%mod*b[(a[x]-e-1)+(a[y]-f)][a[y]-f])%mod;
}
void dfs(int x)
{
int t,i,j,k;
dp1[x][1][0]=dp2[x][1][0]=dp2[x][0][0]=0;
dp1[x][0][0]=dp1[x][2][0]=dp2[x][2][0]=a[x]=1;
for(k=0;k<v[x].size();k++)
{
t=v[x][k];
dfs(t);
for(i=0;i<3;i++)
{
memset(t1[i],0,sizeof(int)*(a[x]+a[t]));
memset(t2[i],0,sizeof(int)*(a[x]+a[t]));
}
for(i=0;i<a[x];i++)
for(j=0;j<=a[t];j++)
{
sum(x,t,2,2,1,i,j,0,a[t]-1);
sum(x,t,2,2,0,i,j,j,a[t]-1);
sum(x,t,1,1,2,i,j,0,a[t]-1);
sum(x,t,1,1,1,i,j,0,a[t]-1);
sum(x,t,1,0,2,i,j,0,j-1);
sum(x,t,0,0,2,i,j,j,a[t]-1);
sum(x,t,0,0,1,i,j,0,a[t]-1);
}
a[x]+=a[t];
for(i=0;i<3;i++)
{
memcpy(dp1[x][i],t1[i],sizeof(int)*a[x]);
memcpy(dp2[x][i],t2[i],sizeof(int)*a[x]);
}
}
for(i=0;i<3;i++)
for(j=1;j<a[x];j++)
{
dp1[x][i][j]=(dp1[x][i][j]+dp1[x][i][j-1])%mod;
dp2[x][i][j]=(dp2[x][i][j]+dp2[x][i][j-1])%mod;
}
}
int main()
{
int t,ans,x,i,j;
for(i=0;i<2000;i++)
{
b[i][0]=1;
for(j=1;j<=i;j++)
b[i][j]=(b[i-1][j-1]+b[i-1][j])%mod;
}
scanf("%d",&t);
while(t--)
{
ans=0;
scanf("%d",&n);
for(i=0;i<n;i++)
v[i].clear();
for(i=1;i<n;i++)
{
scanf("%d",&x);
v[x].push_back(i);
}
dfs(0);
ans=(ans+dp2[0][1][n-1]+dp2[0][2][n-1])%mod;
printf("%d\n",ans);
}
return 0;
}

京公网安备 11010502036488号