本文主要是介绍[ARC121E]Directed Tree,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Directed Tree
题解
题目相当于要求 i i i不能在点 i i i的后代的节点上出现。
如果直接求满足所有条件的树的个数是不大好求的,我们可以先将它转化一下,求不满足条件的树的个数。
我们先定义 d p i , j dp_{i,j} dpi,j表示在 i i i的子树上放置了 j j j个不满足条件的点的方案数,容易得到转移方程式
d p u , j = ∑ v ∈ V u ∑ k = 0 j d p u , j − k d p v , k dp_{u,j}=\sum_{v\in V_{u}}\sum_{k=0}^{j}dp_{u,j-k}dp_{v,k} dpu,j=v∈Vu∑k=0∑jdpu,j−kdpv,k
由于每一对点只会被枚举到一次,所以总的 d p dp dp转移时间复杂度是 O ( n 2 ) O\left(n^2\right) O(n2)。
答案可以通过容斥求出,
A n s = ∑ i = 0 n ( − 1 ) i d p 1 , i ( n − i ) ! Ans=\sum_{i=0}^{n}(-1)^idp_{1,i}(n-i)! Ans=i=0∑n(−1)idp1,i(n−i)!
乘上 ( n − i ) ! (n-i)! (n−i)!代表除了已经不合格的点以外其它点的选择方式,但由于其它点随便选也有可能导致产生更多的点不合格,所以我们要进行容斥。
时间复杂度 O ( n 2 ) O\left(n^2\right) O(n2)。
源码
#include<bits/stdc++.h>
using namespace std;
#define MAXN 2005
#define lowbit(x) (x&-x)
#define reg register
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const LL INF=0x3f3f3f3f3f3f3f3f;
const int mo=998244353;
const int zero=500;
const LL jzm=2333;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
typedef pair<int,int> pii;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){_T f=1;x=0;char s=getchar();while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int add(int x,int y){return x+y<mo?x+y:x+y-mo;}
int n,head[MAXN],tot,dp[MAXN][MAXN],siz[MAXN],tmp[MAXN],fac[MAXN],ans;
struct edge{int to,nxt;}e[MAXN<<1];
void addEdge(int u,int v){e[++tot]=(edge){v,head[u]};head[u]=tot;}
void dosaka(int u,int fa){dp[u][0]=1;for(int i=head[u];i;i=e[i].nxt){int v=e[i].to;dosaka(v,u);for(int j=0;j<=siz[u];j++)for(int k=0;k<=siz[v];k++)tmp[j+k]=add(tmp[j+k],1ll*dp[u][j]*dp[v][k]%mo);for(int j=0;j<=siz[u]+siz[v];j++)dp[u][j]=tmp[j],tmp[j]=0;siz[u]+=siz[v];}for(int j=siz[u];j>=0;j--)dp[u][j+1]=add(dp[u][j+1],1ll*(siz[u]-j)*dp[u][j]%mo);siz[u]++;
}
signed main(){read(n);for(int i=2,p;i<=n;i++)read(p),addEdge(p,i);dosaka(1,0);fac[0]=1;for(int i=1;i<=n;i++)fac[i]=1ll*fac[i-1]*i%mo;for(int i=0;i<=n;i++)if(i&1)ans=add(ans,mo-1ll*dp[1][i]*fac[n-i]%mo);else ans=add(ans,1ll*dp[1][i]*fac[n-i]%mo);printf("%d\n",ans);return 0;
}
谢谢!!!
这篇关于[ARC121E]Directed Tree的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!