[ARC163D] Sum of SCC
思路
有向图 $G(V,E)$ 中的强连通分量数量等于以下值:
- 将 $G$ 的顶点集 $V$ 分成两个顶点集 $A$ 和 $B$ 的方案数,满足以下条件:$A$ 中的顶点与 $B$ 中的顶点之间的每条边都是从 $A$ 指向 $B$,且 $B$ 非空。
证明:设 $s_1,s_2 ,…,s_k$ 是 $G$ 的强连通分量(编号小的强连通分量连出边指向编号大的),那么满足条件的恰好 $k$ 对 $A={s_1,s_2,…,s_i},B={s_{i+1},s_{i+2},…,s_k}$ 有 $0\leq i < k$。
转化后的问题利用动态规划求解。$dp_{i,j,k}(i \leq N,j \leq i, k \leq \min(M,\frac{i\times(i-1)}{2}))$:对于一个 $i$ 个顶点的锦标赛图 $G$ 和顶点集 $A,B$,满足 $|A|=i$,且有 $j$ 条边从较小编号的顶点指向较大编号的顶点的对数。
这里采用刷表法转移,方法如下:
-
$i+1$ 加入 $A$ 集合。
- $i+1$ 与 $A$ 之间的边方向任意。
- $i+1$ 指向所有 $B$ 中的点。
- 枚举变量 $t(t \leq j)$ 表示 $A$ 中指向 $i+1$ 的边数。转移方程:$dp_{i+1,j+1,k+t}$ 加上 $dp_{i,j,k}\times\tbinom{j}{t}$。
-
$i+1$ 加入 $B$ 集合。
- $A$ 中所有节点指向 $i+1$。
- $i+1$ 与 $B$ 之间的边方向任意。
- 枚举 $B$ 中指向 $i+1$ 的边数 $t(t \leq i - j)$。转移方程:$dp_{i+1,j,k+t+j}$ 加上 $dp_{i,j,k}\times\tbinom{i-j}{t}$。
时间复杂度:$O(N^3M)$。
代码
#include <bits/stdc++.h>
#define L(i, a, b) for(int i = (a); i <= (b); i++)
#define R(i, a, b) for(int i = (a); i >= (b); i++)
using namespace std;
const int N = 32, M = 440, mod = 998244353;
int n, m, ans, C[N][N], dp[N][N][M];
int main(){
scanf("%d%d", &n, &m), C[0][0] = 1;
L(i, 1, n){
C[i][0] = 1;
L(j, 1, i) C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
}
dp[0][0][0] = 1;
L(i, 0, n - 1) L(j, 0, i) L(k, 0, min(m, i * (i - 1) / 2)){
L(t, 0, min(j, m - k)) (dp[i + 1][j + 1][k + t] += 1ll * dp[i][j][k] * C[j][t] % mod) %= mod;
L(t, 0, min(i - j, m - k - j)) (dp[i + 1][j][k + t + j] += 1ll * dp[i][j][k] * C[i - j][t] % mod) %= mod;
}
L(i, 0, n - 1) (ans += dp[n][i][m]) %= mod;
printf("%d", ans);
return 0;
}