国庆集训 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]\times 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]\times 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推错了,以后注意要仔细揣摩样例与题意

posted @ 2019-10-01 19:10  Santiego  阅读(171)  评论(0编辑  收藏  举报