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. 新加入一个孤立点:\(1\leq l\)\(dp[i][j]+=dp[i-1][j]\)

  2. 新加入一条链:枚举 \(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

  3. 新加入只有两个点的环:\(2\leq l\)\(dp[i][j]+=dp[i-2][j-2]*(n-k-1)\)

    这里不能乘 \(\binom{n-k}{2}\) ,因为要先提前拿出一个起点。

  4. 新加入一个环:枚举 \(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;
}
posted @ 2021-11-08 20:45  _Famiglistimo  阅读(75)  评论(0编辑  收藏  举报