题目大意
这道题从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; }