ABC180F Unbranched 题解
ABC180F Unbranched 题解
Statement
统计方案数:构造一个 \(n\) 个有标号点,\(m\) 条无标号边的图,图不一定是简单图,不一定联通,但满足以下条件:
- 无自环
- 每个点的度不超过 \(2\)
- 最大联通块大小为 \(L\)
答案很大,你忍一下 取模 \(10^9+7\)
其中, \(2\leq n\leq 300,1\leq m,L\leq n\) ,且都为整数
样例输入 3 2 3
样例输出 3
Solution
参考 Editorial - AtCoder Beginner Contest 180
很重要一个限制叫做度不超过 \(2\)
显然,最后形成的图会由一些 孤立点/链/环组成
对于第三个限制,其等价于限制最大联通块大小最多为 \(L\) 的方案数 减去 最大联通块大小最多为 \(L-1\) 的方案数
考虑记 \(dp_l[i][j]\) 表示在大小最多为 \(l\) 的限制下,已经构造了一个点数 \(n\) ,边数 \(m\) 的合法图的方案数,考虑转移
-
新加入一个孤立点:\(1\leq l\) ,\(dp[i][j]+=dp[i-1][j]\)
-
新加入一条链:枚举 \(k\in[2,l]\) ,$dp[i][j]+=dp[i-k][j-k+1]\binom{n-i+k-1}{k-1}k!/2 $
也即是说,新加入 \(k\) 个点,\(k-1\) 条边。这 \(k\) 个点在剩下的 \(n-i+k\) 个点中选出,但是考虑到可能选重,参见 ABC226F Score of Permutations - lost_heart_hurts ,所以要预先拿出一个点作为链的起点,成了 \(\binom{n-i+k-1}{k-1}\) ,然后链内点的顺次随便,考虑到一条链换一个方向也还是一条链,所以要除 2
-
新加入只有两个点的环:\(2\leq l\),\(dp[i][j]+=dp[i-2][j-2]*(n-k-1)\)
这里不能乘 \(\binom{n-k}{2}\) ,因为要先提前拿出一个起点。
-
新加入一个环:枚举 \(k\in[3,l]\) ,那么 \(dp[i][j]+=dp[i-k][j-k]*\binom{n-i+k-1}{k-1}*(k-1)!/2\)
可以结合 2 3 理解。特别的,这里应该是 \(k-1\) 的阶乘,因为我们固定了起终点必有边(相邻)
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e2+5;
const int mod = 1e9+7;
const int inv2 = (mod+1)/2;
int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
return s*w;
}
int dp[N][N],jc[N],invj[N];
int n,m,l,ans;
int ksm(int n,int m){int r=1;while(m)((m&1)&&(r=r*n%mod,1)),n=n*n%mod,m>>=1;return r;}
int C(int n,int m){return (n>=0&&m<=n)?jc[n]*invj[m]%mod*invj[n-m]%mod:0;}
int solve(int limit){
memset(dp,0,sizeof(dp)),dp[0][0]=1;
for(int i=0;i<=n;++i,puts(""))
for(int j=0;j<=m;cout<<dp[i][j]<<" ",++j)
for(int k=1;k<=min(i,limit);++k){
if(k>1&&k<=j){
int tmp=dp[i-k][j-k]*C(n-i+k-1,k-1)%mod*jc[k-1]%mod;
if(k>2)(tmp*=inv2)%=mod;
(dp[i][j]+=tmp)%=mod;
}
if(k<=j+1){
int tmp=dp[i-k][j-k+1]*C(n-i+k-1,k-1)%mod*jc[k]%mod;
if(k>1)(tmp*=inv2)%=mod;
(dp[i][j]+=tmp)%=mod;
}
}
return dp[n][m];
}
signed main(){
n=read(),m=read(),l=read();
for(int i=jc[0]=1;i<=n;++i)jc[i]=jc[i-1]*i%mod;
invj[n]=ksm(jc[n],mod-2);
for(int i=n-1;~i;--i)invj[i]=invj[i+1]*(i+1)%mod;
printf("%lld\n",(solve(l)-solve(l-1)+mod)%mod);
return 0;
}