ARC121E Directed Tree

ARC121E Directed Tree

有意思的容斥加树 dp。

思路

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

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

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

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

ans=i=0n(1)igi(ni)!

(ni)!ni 个位置可以随便放 ni 个数。

gi 考虑树 dp,设 fu,iu 子树内有 i 个点不合法的方案数(只考虑不合法的点)。

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

fu,j+k=fv,j×fv,j

v,vu 的儿子。

但这时求出来的 fuu 没选择的结果,加上 u 位置的选择:

fu,i=fu,i1×((szu1)(i1))

szu1 个点可选,i1 个点被选了。

那么 gi=f1,i

时间复杂度 O(n2)

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);
}
posted @   彬彬冰激凌  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示