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