[ARC163D] Sum of SCC 题解
[ARC163D] Sum of SCC
给定 \(n,m\),询问 \(n\) 个点的竞赛图中满足恰好有 \(m\) 条边是从小连到大的竞赛图的 SCC 个数之和,\(n\le 30\)。
欲做此题必先发现一个结论,即竞赛图的 SCC 个数为满足条件 \(1\) 的划分数减一。条件 \(1\) 为把图划分成 \(A\) 和 \(B\) 两部分(可为空集),对于任意的 \(u\in A,v \in B\),有 \(u \to v\)。
知道结论以后设计状态 DP,设 \(f_{i,j,k}\) 表示当前 \(A\) 集合划分了 \(i\) 个点,\(B\) 集合划分了 \(j\) 个点,已经有了 \(k\) 条从小连到大的边,那我考虑新加入一个点 \(i+j+1\),这个点的编号是目前最大的。
假设加入 \(A\),则因为 \(AB\) 之间只有 \(A \to B\),所以这个点也是连向 \(B\),然后和 \(A\) 内部连边情况任意,枚举连 \(x\) 条小到大的边,则转移方程为 \(f_{i,j,k}\times \binom{i}{x} \to f_{i+1,j,k+x}\)。
假设加入 \(B\),则 \(A\) 中的所有点都要向此点连边,从小到大的边新增 \(i\) 条,还是枚举内部连边情况,方程为 \(f_{i,j,k}\times \binom{j}{x} \to f_{i,j+1,k+i+x}\)。
最后答案为 \(\sum_{i=0}^n f_{i,n-i,m} - \binom{ \frac{n(n+1)}{2}}{m}\),后面那项是有几个满足条件的竞赛图,因为每一个都要减一。
时间复杂度 \(O(n^3m)\)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
int n,m;
const int N = 35,mod=998244353;
int f[N][N][N*N],fac[N*N],inv[N*N];
int ans=0;
int qpow(int a,int b){
int res=1;
while(b){
if(b&1)res=(res*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return res;
}
int C(int n,int m){
if(n<m)return 0;
return fac[n]%mod*inv[m]%mod*inv[n-m]%mod;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
fac[0]=inv[0]=1;
for(int i=1;i<=n*n;i++){
fac[i]=(fac[i-1]*i)%mod;
}
inv[n*n]=qpow(fac[n*n],mod-2);
for(int i=n*n-1;i>=0;i--){
inv[i]=(inv[i+1]*(i+1))%mod;
}
f[0][0][0]=1;
for(int i=0;i<n;i++){
for(int j=0;j<=n;j++){
if(i+j>=n)break;
for(int k=0;k<=m;k++){
for(int x=0;x<=i;x++){
(f[i+1][j][k+x]+=f[i][j][k]%mod*C(i,x)%mod)%=mod;
}
for(int x=0;x<=j;x++){
(f[i][j+1][k+i+x]+=f[i][j][k]%mod*C(j,x)%mod)%=mod;
}
}
}
}
ans=(mod-C(n*(n-1)/2,m))%mod;
for(int i=0;i<=n;i++){
ans=(ans+f[i][n-i][m])%mod;
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号