[洛谷P5031]庞氏骗局

题目

传送门 to luogu

题目概要
K 1 K_1 K1 1 1 1 万元和 K 2 K_2 K2 2 2 2 万元,每一份钱是不同的。每一天都可以选择两份钱,各消费 1 1 1 万元。每天选择的两份钱不能与之前重复。求花光所有的钱,共有多少不同的方案集合。方案没有先后顺序,每一天不分先后。

输入格式
一行,两个整数,分别是 K 1 , K 2 K_1,K_2 K1,K2

输出格式
一行,一个整数,表示方案数。

数据范围与约定
数据范围的图片

思路

可以将原题目转化成:

在一张简单无向图上有 K 1 K_1 K1 个出度为 1 1 1 K 2 K_2 K2 个出度为 2 2 2 的点,每个点的标号都不一样,求图的总可能方案数

对于这样的一个图,花费两份钱,等价于删掉一条边,并把两个端点代表的钱各花费 1 1 1 万元。“两份钱不能重复” ⇒ \Rightarrow 无重边;“花两份钱” ⇒ \Rightarrow 无自环;每一天不分先后 ⇒ \Rightarrow 边没有编号。

如果一个方案合法,一定可以建出一个这样的图;反之,建立的图一定不是这样的。

如果图满足该条件,一定是一个合法的方案;反之,一定不是合法的方案。

所以,这种图和可能的方案是一一对应的。

容易发现,最后一定是由若干个环和若干条链组成的图,像这样:

PPL

每一条链的两端都是出度为一的点,易得若图有方案,则出度为一的点个数必为偶数, i . e .    2 ∣ K 1 {\tt i.e.\;}2|K_1 i.e.2K1

我们先考虑特殊情况。从简单情况入手往往会有助于解题。

都是一万元

K 2 = 0 K_2=0 K2=0 时,只有两个点的小线段。考虑给每个点选另一个端点,第一次有 K 1 − 1 K_1-1 K11 个点可供选择,第二次就只有 K 1 − 3 K_1-3 K13 个了(不能拆开第一步已经连接好的两个点)……

于是,当 K 2 = 0 K_2=0 K2=0 时,方案数为

Q = ( K 1 − 1 ) ( K 1 − 3 ) ( K 1 − 5 ) × ⋯ × 3 × 1 {\mathcal Q}=(K_1-1)(K_1-3)(K_1-5)\times\cdots\times 3\times 1 Q=(K11)(K13)(K15)××3×1

请记住这个方案数 Q \mathcal Q Q ,下方仍然会使用该记号。

都是两万元

K 1 = 0 K_1=0 K1=0 时,考虑 d p \tt{dp} dp 。记 f ( i , j ) f(i,j) f(i,j) i i i 个点组成 j j j 个环的方案数,则有

f ( i , j ) = ( i − 1 ) f ( i − 1 , j ) + ( i − 1 2 ) f ( i − 3 , j − 1 ) f(i,j)=(i-1)f(i-1,j)+{i-1\choose{2}}f(i-3,j-1) f(i,j)=(i1)f(i1,j)+(2i1)f(i3,j1)

那么 K 2 K_2 K2 的总方案数就是 ∑ i = 1 + ∞ f ( K 2 , i ) \sum^{+\infty}_{i=1}f(K_2,i) i=1+f(K2,i)

d p \tt{dp} dp 边界是 f ( 0 , 0 ) = 1 f(0,0)=1 f(0,0)=1 ,没有点也没有环。

接下来我们解释上面的 d p \tt{dp} dp 式子。

f ( i , j ) f(i,j) f(i,j) 的方案有两种可能:

  • 把第 i i i 个点插入已有的 j j j 个环中,可由 f ( i − 1 , j ) f(i-1,j) f(i1,j)​ 转移而来。
    ZZI
    可以插在任意一个环中的任意两个节点之间,故该情况的方案数为 ( i − 1 ) f ( i − 1 , j ) (i-1)f(i-1,j) (i1)f(i1,j) 。显然 i i i 所在的环的大小会超过 3 3 3

  • 从已有的 i − 1 i-1 i1 个点中选出 2 2 2 个点与点 i i i 组成一个新的环,剩下的 i − 3 i-3 i3 点再组成 j − 1 j-1 j1 个环的方案数,故该情况的方案数为 ( i − 1 2 ) f ( i − 3 , j − 1 ) {i-1\choose{2}}f(i-3,j-1) (2i1)f(i3,j1) 。为什么新环的大小为 3 3 3 呢?因为如果新环更大,就相当于点 i i i 插入一个环中,与之前的情况重复。
    Nice To Meet You, The Thief

特殊情况考虑完了,接下来把两种情况综合到一起。我们发现,其实总方案数就是

Q × ∑ i = 0 K 2 {\mathcal Q}\times\sum_{i=0}^{K_2} Q×i=0K2(将 i i i 个点放入 K 1 2 K_1 \over 2 2K1 条长度为 2 2 2 的链中的方案数) × \times ×(余下 K 2 − i K_2-i K2i 个点组成若干个环的方案数)

而将 i i i 个点放入 j j j 条长度为 2 2 2 的链中的方案数为

j ( j + 1 ) ( j + 2 ) ⋯ ( j + i − 1 ) = ( j + i − 1 ) ! ( j − 1 ) ! j(j+1)(j+2)\cdots(j+i-1)={(j+i-1)! \over (j-1)!} j(j+1)(j+2)(j+i1)=(j1)!(j+i1)!

即将第 1 1 1 个点放入有 j j j 个空可以放,故有 j j j 种方案;将第 2 2 2 个点放入有 j + 1 j+1 j+1 个空可以放,故有 j + 1 j+1 j+1 种方案……以此类推。

f f f 数组的预处理是 O ( n 2 ) \mathcal O(n^2) O(n2) 的, Q \mathcal Q Q 可以用 O ( n ) \mathcal O(n) O(n) 算,所以总时间复杂度是 O ( n 2 ) \mathcal O(n^2) O(n2) 的。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7,N=6e3;
int k1,k2;
int sum1,zs1;
int f[N][2005];
int jc[N<<1],inv[N<<1];
int QP(int x,int k){
    int ret=1;
    while(k){
        if(k&1)ret=ret*x%mod;
        k>>=1;
        x=x*x%mod;
    }
    return ret;
}
signed main(){
    scanf("%lld%lld",&k1,&k2);
    if(k1&1){puts("0");return 0;}
    zs1=k1>>1;
    sum1=1;
    for(int i=k1-1;i>=0;i-=2)sum1=(sum1*i)%mod;
    jc[0]=1;
    f[0][0]=1;
    for(int i=1;i<=k2+zs1;++i)jc[i]=jc[i-1]*i%mod;
    inv[k2+zs1]=QP(jc[k2+zs1],mod-2);
    for(int i=k2+zs1-1;i>=0;--i)inv[i]=inv[i+1]*(i+1)%mod;
    for(int i=2;i<=k2;++i){
        int x=((i-1)*(i-2)>>1)%mod;
        for(int j=1;j<=i/3;++j){
            f[i][j]=f[i-1][j]*(i-1)%mod;
            (f[i][j]+=(f[i-3][j-1]*x)%mod)%=mod;
        }
    }
    for(int i=3;i<=k2;++i)
        for(int j=1;j<=i/3;++j)
            (f[i][0]+=f[i][j])%=mod;
    int ans=0;
    if(!k2)ans=sum1;
    else if(!k1)ans=f[k2][0];
    else{
        int ret=0,cs=inv[zs1-1]*jc[k2]%mod;
        for(int i=0;i<=k2;++i){
            ret=inv[i]*inv[k2-i]%mod*jc[zs1+i-1]%mod*f[k2-i][0]%mod;
            (ans+=ret)%=mod;
        }
        (ans*=sum1*cs%mod)%=mod;
    }
    printf("%lld",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值