ARC121E Directed Tree / 二项式反演 容斥

题目传送门:ARC121E Directed Tree

首先,如果 \(i\) 满足条件,那么 \(a_i\) 不为 \(i\) 的祖先(注意 \(a_i=i\) 满足条件),设 \(g_i\) 表示钦定 \(i\) 个位置不满足的方案数。

考虑 dp,设 \(f_{i,j}\) 表示以 \(i\) 为根的子树,钦定了 \(j\) 个位置且只考虑这 \(j\) 个位置的方案数。

转移分两种情况。

  1. 从儿子转移 \(f_{u,i}\times f_{v,j} \to f'_{u,i+j}\)
  2. \(u\) 的子树中找一个位置钦定且 \(a\)\(u\)\(f_{u,i-1}\times [(sz_u-1)-(i-1)] \to f'_{u,i}\)

因此 \(g_i=f_{1,i}\times (n-i)!\),没钦定的可以随便选。

最后答案直接二项式反演 \(ans=\sum_{i=0}^n (-1)^ig_i\)

注意转移时枚举的顺序,具体的看代码。

#include<bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
const int N=2010,mod=998244353; 
inline int read(){
	char c=getchar();
	int f=1,ans=0;
	while(c<48||c>57) f=(c==45?f=-1:1),c=getchar();
	while(c>=48&&c<=57) ans=(ans<<1)+(ans<<3)+(c^48),c=getchar();
	return ans*f;
}
int n;vector<int>g[N];
int f[N][N],jc[N],tmp[N],sz[N];
void dfs(int u){
	f[u][0]=sz[u]=1;
	for (auto v:g[u]){
		dfs(v);
		for (int i=0;i<=sz[u]+sz[v];i++) tmp[i]=0;
		for (int i=0;i<=sz[u];i++) for (int j=0;j<=sz[v];j++) if (i+j<=n) tmp[i+j]=(tmp[i+j]+f[u][i]*f[v][j]%mod)%mod;
		for (int i=0;i<=sz[u]+sz[v];i++) f[u][i]=tmp[i];
		sz[u]+=sz[v];
	}
	for (int i=sz[u];i;i--) f[u][i]=(f[u][i]+f[u][i-1]*(sz[u]-i)%mod)%mod;
}
main(){
	n=read();
	for (int i=2;i<=n;i++) g[read()].push_back(i);
	jc[0]=1;for (int i=1;i<=n;i++) jc[i]=jc[i-1]*i%mod;
	dfs(1);
	int ans=0;
	for (int i=0;i<=n;i++){
		if (i&1) ans=(ans-f[1][i]*jc[n-i]%mod+mod)%mod;
		else ans=(ans+f[1][i]*jc[n-i]%mod)%mod;
	}
	cout <<ans;
    return 0;
}
posted @ 2026-01-22 20:31  OTn53_qwq  阅读(17)  评论(0)    收藏  举报