国庆集训 Day1 T2 生成图 DP

国庆集训 Day1 T2 生成图

现在要生成一张(n)个点的有向图。要求满足:
1.若有 a->b的边,则有 b->a 的边
2.若有 a->b 的边和 b->c 的边,则有 a->c 的边
3.至少有一个点没有自环。
求方案数模上(m)
(n≤2000,2≤m≤1,000,000,007)

样例:
input
2 5
output
3

有点难度的DP,首先需要明确的是在一个连通图中每一个点都有自环(样例可体现),所以有点没有自环当且仅当这一个点独立为一个联通块

(g[i])表示(i)个点自由组合,且每个点都存在自环的方案数,(f[i])表示(i)个点自由组合,且至少有1个点没有自环的方案数((f[n])即答案)

考虑(f[i])转移有以下情况:

  • (i)个点孤立且没有自环,即(f[i]+=f[i-1]+g[i-1])
  • (i)个点孤立且自环,即(f[i]+=f[i-1])
  • (i)个点与前(i-1)个点中的(j-1)个点构成一个大小为(j)的联通块,即(f[i]+=sum_{j=2}^{i-1}f[i-j] imes C_{i-1}^{j-1})

考虑(g[i])转移有以下情况:

  • (i)个点孤立且自环,即(g[i]+=g[i-1])
  • 类似的,第(i)个点与前(i-1)个点中的(j-1)个点构成一个大小为(j)的联通块,即(g[i]+=sum_{j=2}^{i-1}g[i-j] imes C_{i-1}^{j-1})

为求(C_n^m),我们可以利用杨辉三角,第(i)行第(j)列((i)从0开始)即为(C_i^j)

#include <cstdio>
#define MAXN 2002
#define ll long long
using namespace std;
ll C[MAXN][MAXN]; //C[n][m]
ll f[MAXN],g[MAXN];
int n,MOD;
int main(){
    scanf("%d %d", &n, &MOD);
    for(int i=0;i<=n;++i){
        C[i][0]=1;
        for(int j=1;j<=i;++j)
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
    }
    f[1]=1;
    g[1]=1;
    for(int i=2;i<=n;++i){
        g[i]=g[i-1]%MOD;
        for(int j=2;j<=i-1;++j)
            g[i]=(g[i]+g[i-j]*C[i-1][j-1]%MOD)%MOD;
        g[i]=(g[i]+1)%MOD;
        f[i]=(f[i-1]+f[i-1]+g[i-1])%MOD;
        for(int j=2;j<=i-1;++j)
            f[i]=(f[i]+f[i-j]*C[i-1][j-1]%MOD)%MOD;
    }
    printf("%lld", f[n]);
    return 0;
}

一开始题读错了导致后面DP推错了,以后注意要仔细揣摩样例与题意

原文地址:https://www.cnblogs.com/santiego/p/11615869.html