给定正整数 (n,m)。初始有一张有向图,仅包含两个节点 (s,t) 和一条边 (s ightarrow t)。你可以做以下操作 (n) 次:
- 选择 (u ightarrow v),加入节点 (w) 和 (u ightarrow w ightarrow v) 两条边。
求在所有能得到的无标号但区分源汇点 (s,t) 的图中,(s) 到 (t) 的最大流为 (m) 的数量模 (10^9+7) 的值。
(n,mle 50)。
一看上去就很 dp(
考虑串并联图的子问题性质。先考虑并联,于是设 (f_{i,j}) 表示操作 (i) 次之后最大流为 (j) 的图的数量,(g_{i,j}) 表示只考虑 (s ightarrow w ightarrow t) 这一条链,操作 (i) 次之后最大流为 (j) 的图的数量。
(f ightarrow g) 是 ((min,+)) 卷积,(g ightarrow f) 就对于按顺序枚举 ((i,j)) 和其出现次数 (k),那么方案数是 (g_{i,j}^{kuparrow}/k!)。
时间复杂度是 soft (O(n^4)),但是跑 100 好像不到 1s(?
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 53, mod = 1e9 + 7;
int n, m, f[N][N], g[N][N], h[N][N], F[N][N], inv[N];
void qmo(int &x){x += x >> 31 & mod;}
int main(){
scanf("%d%d", &n, &m);
if(m == n+1){puts("1"); return 0;}
if(m > n+1){puts("0"); return 0;}
f[0][1] = F[0][1] = inv[1] = 1;
for(int i = 2;i <= 50;++ i) inv[i] = mod - (LL)mod / i * inv[mod % i] % mod;
for(int i = 1;i <= n;++ i){
for(int j = 0;j <= n;++ j)
for(int k = 1;k <= i;++ k)
h[i][j] = (h[i][j] + (LL)F[i-k][j] * F[k-1][j]) % mod;
for(int j = 1;j <= n;++ j) qmo(g[i][j] = h[i][j] - h[i][j+1]);
for(int j = 1;j <= n;++ j)
for(int k = n;k >= i;-- k)
for(int l = n;l >= j;-- l)
for(int _ = 1, pw = 1;_*i <= k && _*j <= l;++ _){
pw = (g[i][j] + _ - 1ll) * pw % mod * inv[_] % mod;
f[k][l] = (f[k][l] + (LL)pw * f[k-_*i][l-_*j]) % mod;
}
for(int j = n;j;-- j) qmo(F[i][j] = F[i][j+1] + f[i][j] - mod);
} printf("%d
", f[n][m]);
}