ARC121E Directed Tree

发布时间 2023-12-07 17:38:51作者: 彬彬冰激凌

ARC121E Directed Tree

有意思的容斥加树 dp。

思路

\(a_i\) 可以是除去 \(i\) 祖先之外的所有点,考虑 \(a_i\) 的逆排列。

每一个 \(i\) 在正排列里都可以被不是自己子树内的点选择,那么逆排列里 \(i\) 不可以放自己子树内的点(不包括自己)。

现在转换求逆排列的方案数。

考虑容斥,设 \(g_i\) 为有 \(i\) 个位置不合法的方案数。

\(ans=\sum_{i=0}^n (-1)^ig_i(n-i)!\)

\((n-i)!\)\(n-i\) 个位置可以随便放 \(n-i\) 个数。

\(g_i\) 考虑树 dp,设 \(f_{u,i}\)\(u\) 子树内有 \(i\) 个点不合法的方案数(只考虑不合法的点)。

因为 \(u\) 的不同子树,不合法的范围不相交,有:

\[f_{u,j+k}=f_{v,j}\times f_{v',j} \]

\(v,v'\)\(u\) 的儿子。

但这时求出来的 \(f_u\)\(u\) 没选择的结果,加上 \(u\) 位置的选择:

\[f_{u,i}=f_{u,i-1}\times((sz_u-1)-(i-1)) \]

\(sz_u-1\) 个点可选,\(i-1\) 个点被选了。

那么 \(g_i=f_{1,i}\)

时间复杂度 \(O(n^2)\)

CODE

#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define mod 998244353

const int maxn=2e3+5;

int n;
int siz[maxn];

ll ans;
ll fac[maxn],f[maxn][maxn],g[maxn];

vector<int>E[maxn];

void dfs(int u)
{
    f[u][0]=1,siz[u]=1;
    for(auto v:E[u]) dfs(v);
    for(auto v:E[u])
    {
        for(int i=0;i<=siz[u]+siz[v];i++) g[i]=0;
        for(int i=0;i<=siz[u];i++) for(int j=0;j<=siz[v];j++) g[i+j]=(g[i+j]+f[u][i]*f[v][j]%mod)%mod;
        for(int i=0;i<=siz[u]+siz[v];i++) f[u][i]=g[i];
        siz[u]+=siz[v];
    }
    for(int i=siz[u];i>=1;i--) f[u][i]=(f[u][i]+f[u][i-1]*(siz[u]-i)%mod)%mod;
}

int main()
{
    scanf("%d",&n);
    fac[0]=1;
    for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
    for(int i=2;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        E[x].push_back(i);
    }
    dfs(1);
    for(int i=0;i<=n;i++) f[1][i]=f[1][i]*fac[n-i]%mod;
    for(int i=0;i<=n;i++)
    {
        ll k=1;
        if(i&1) k=-1;
        ans=(ans+k*f[1][i])%mod;
    }
    printf("%lld",(ans+mod)%mod);
}