CF848D Shake It!【分析性质,dp】

给定正整数 \(n,m\)。初始有一张有向图,仅包含两个节点 \(s,t\) 和一条边 \(s\rightarrow t\)。你可以做以下操作 \(n\) 次:

  • 选择 \(u\rightarrow v\),加入节点 \(w\)\(u\rightarrow w\rightarrow v\) 两条边。

求在所有能得到的无标号但区分源汇点 \(s,t\) 的图中,\(s\)\(t\) 的最大流为 \(m\) 的数量模 \(10^9+7\) 的值。

\(n,m\le 50\)


一看上去就很 dp(

考虑串并联图的子问题性质。先考虑并联,于是设 \(f_{i,j}\) 表示操作 \(i\) 次之后最大流为 \(j\) 的图的数量,\(g_{i,j}\) 表示只考虑 \(s\rightarrow w\rightarrow t\) 这一条链,操作 \(i\) 次之后最大流为 \(j\) 的图的数量。

\(f\rightarrow g\)\((\min,+)\) 卷积,\(g\rightarrow f\) 就对于按顺序枚举 \((i,j)\) 和其出现次数 \(k\),那么方案数是 \(g_{i,j}^{k\uparrow}/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\n", f[n][m]);
}
posted @ 2021-04-21 16:52  mizu164  阅读(79)  评论(0编辑  收藏  举报