[ABC180F] Unbranched

题目传送门

A T AT AT 的题面大多数情况下都挺人性的

解法

考虑分析三个限制:
− - 图中无自环 : 对于构造图时特判大小为 1 1 1 的环
− - 每个点度数最多为 2 2 2 : 仔细想想,这个限制翻译过来就是图中每一个连通块是一个 或者 , 不可能是 团 或者 树
− - 所有连通块最多恰好有 L L L 个点 : : : 没什么好说的, D P DP DP 的时候限制一下或者装进 D P DP DP 状态里转移

设计状态 :

f i , j : 选 i 个点,构成的图中联通块的大小不超过 j 的合法方案数 f_{i,j} :选i个点,构成的图中联通块的大小不超过j的合法方案数 fi,j:i个点,构成的图中联通块的大小不超过j的合法方案数
那么 a n s = f n , m ans=f_{n,m} ans=fn,m

状态转移 :

分别讨论加入的新的联通块是链或环的情况即可

f i , j ← ∑ k = 1 min ⁡ { i , j + 1 , L } f i − k , j − ( k − 1 ) ∗ C k − 1 n − ( i − k ) − 1 ∗ k ! 2 注意 k = 1 的情况单独讨论 f_{i,j} \gets \sum_{k=1}^{\min\{i,j+1,L\}}f_{i-k,j-(k-1)} *C^{n-(i-k)-1}_{k-1} *\frac{k!}{2}\\ 注意k=1的情况单独讨论 fi,jk=1min{i,j+1,L}fik,j(k1)Ck1n(ik)12k!注意k=1的情况单独讨论

f i , j ← ∑ k = 2 m i n ( i , j , L ) f i − k , j − k ∗ C k − 1 n − ( i − k ) − 1 ∗ ( k − 1 ) ! 2 一样的,注意 k = 2 的情况单独讨论 f_{i,j} \gets \sum_{k=2}^{min(i,j,L)} f_{i-k,j-k}*C^{n-(i-k)-1}_{k-1} * \frac{(k-1)!}{2}\\ 一样的,注意k=2的情况单独讨论 fi,jk=2min(i,j,L)fik,jkCk1n(ik)12(k1)!一样的,注意k=2的情况单独讨论

Code :
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 307,M=300,INF=0x3f3f3f3f;
const ll mod=1e9+7 ,inv2=500000004;
int n,m,L;
ll ans;
ll f[N][N],fac[N],ifac[N];
void init() {
    for(int i=0;i<=n;i++) for(int j=0;j<=m;j++)
        f[i][j]=0;
}
ll qpow(ll ba,ll pow) {
    ll res=1; while(pow) {
        if(pow&1) res=res*ba%mod;
        ba=ba*ba%mod,pow>>=1;
    } return res;
} 
void pre() {
    fac[0]=1; for(int i=1;i<=M;i++) fac[i]=fac[i-1]*i%mod;
    ifac[M]=qpow(fac[M],mod-2); for(int i=M;i;i--) ifac[i-1]=ifac[i]*i%mod;
}
ll C(ll x,ll y) { return fac[x]*ifac[y]%mod*ifac[x-y]%mod; }
void work(int lim) {
	f[0][0]=1;
    for(int i=1;i<=n;i++) {
        for(int j=0;j<=m;j++) {
            for(int k=1;k<=min(j+1,min(lim,i));k++) {
                f[i][j]=(f[i][j]+f[i-k][j-(k-1)]*C(n-(i-k)-1,k-1)%mod*(k>1?fac[k]*inv2%mod:1)%mod)%mod;
                if(k>1) f[i][j]=(f[i][j]+f[i-k][j-k]*C(n-(i-k)-1,k-1)%mod*(k>2?fac[k-1]*inv2%mod:1)%mod)%mod;
            }
        }
    }
}
int main() {
    scanf("%d%d%d",&n,&m,&L);
    pre();
    work(L),ans=f[n][m];
    init(),work(L-1);
//    printf("%lld\n",ans);
    printf("%lld\n",((ans-f[n][m])%mod+mod)%mod);
}
    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值