[ARC163D] Sum of SCC
对于所有 \(n\) 个点的竞赛图 \(G\),若其满足下面条件,则称其为好图:
- \(G\) 中由编号较小的点指向编号较大的点的边恰好有 \(m\) 条
求所有好图 \(G\) 的强连通分量个数之和,对 \(998244353\) 取模。
\(1 \leq n \leq 30\),\(0 \leq m \leq \dbinom{n}{2}\)。
竞赛图的性质:缩点后形成一条链。
也就是说,对于竞赛图 \(G\),其强连通分量个数等价于下面计数问题的方案数:
- 将点集 \(V\) 划分成两个点集 \(A\) 和 \(B\),使得 \(B\) 非空且不存在 \(B\) 连向 \(A\) 的边
我们令 \(dp_{i,j,k}\) 表示前 \(i\) 个点连 \(k\) 条小到大的边,当前 \(|A| = j\) 的方案数。
接下来考虑转移到第 \(i+1\) 个点,我们枚举其在哪个集合里。
假设其在 \(A\) 集合里,对于前面所有 \(B\) 中的点,只能连从 \(i+1\) 到 \(B\) 的边。
对于前面 \(A\) 中的点,这个随便连,容易转移。
假设其在 \(B\) 集合里,对于前面所有 \(A\) 中的点,只能连从 \(A\) 到 \(i+1\) 的边。
对于前面 \(B\) 中的点,这个随便连,容易转移。
结束了,复杂度 \(O(n^3 m)\)。
#include<iostream>
#include<cstdio>
using namespace std;
const long long mod=998244353;
const int V=30;
long long inv(long long num){
long long pre=mod-2,ans=1;
while(pre){
if(pre&1){
ans=ans*num%mod;
}
num=num*num%mod;
pre>>=1;
}
return ans;
}
long long fact[V+10],invfact[V+10];
void init(){
fact[0]=1;
invfact[0]=1;
for(int i=1;i<=V;i++){
fact[i]=fact[i-1]*i%mod;
invfact[i]=invfact[i-1]*inv(i)%mod;
}
}
inline long long C(int num1,int num2){
if(num2<0 || num2>num1) return 0;
else return fact[num1]*invfact[num2]%mod*invfact[num1-num2]%mod;
}
long long dp[40][40][510];
int main(){
init();
int n,m;
scanf("%d %d",&n,&m);
dp[1][0][0]=dp[1][1][0]=1;
for(int i=1;i<n;i++){
for(int j=0;j<=i;j++){
for(int k=0;k<=i*(i-1)/2;k++){
for(int l=0;l<=j;l++){
dp[i+1][j+1][k+l]+=dp[i][j][k]*C(j,l)%mod;
dp[i+1][j+1][k+l]%=mod;
}
for(int l=0;l<=i-j;l++){
dp[i+1][j][k+l+j]+=dp[i][j][k]*C(i-j,l)%mod;
dp[i+1][j][k+l+j]%=mod;
}
}
}
}
long long ans=0;
for(int i=1;i<=n;i++){
ans+=dp[n][i][m];
ans%=mod;
}
printf("%lld",ans);
return 0;
}

浙公网安备 33010602011771号