The Intriguing Obsession 非DP纯数学题解

来源:Codeforces Round #439 (Div. 2)

Vjudge地址:https://vjudge.net/problem/CodeForces-869C

Examples
Input
1 1 1
Output
8
Input
1 2 2
Output
63
Input
1 3 5
Output
3264
Input
6 2 9
Output
813023575

the second example图片如下:


由样例1可以知道,最终结果res=红蓝桥的方案数×红蓝桥的方案数×红蓝桥的方案数

那么问题就分解为求两种颜色之间小岛方案数。

样例2中,三种颜色的小岛数量分别为1,2,2.

1-2之间有3种搭桥方案(1种不搭桥,2种搭一座桥),2-2之间有7种搭桥方案(1种不搭桥,4种搭一座桥,2种搭两座桥)

故样例2输出为3×3×7=63种方案。

样例3中,三种颜色的小岛数量分别为1,3,5.

1-3之间有4种搭桥方案(1种不搭桥,3种搭一座桥),1-5之间有6种搭桥方案(1种不搭桥,5种搭一座桥),那3-5呢?

通过样例输出可以算出,3-5之间的方案数=3264÷(4×6)=136,那136怎么得来呢?

首先0座桥有1种,1座桥有3×5=15种,那2座桥是C(3,2)×C(5,2)=3×10=30种么?3座桥是C(3,3)×C(5,3)=10种么?

不是,2座桥匹配可以是11,22也可以是12,21,所以要在30的基础上乘2,

3座桥匹配可以是....

其实可以一侧固定顺序,求另一侧有多少的排列方式,数学表达即阶乘,这样就可以总结出来不同桥数的不同方案数。

3-5之间的桥方案数分别为:

0座桥:C(3,0)*C(5,0)*0!=1种

1座桥:C(3,1)*C(5,1)*1!=15种

2座桥:C(3,2)*C(5,2)*2!=60种

3座桥:C(3,3)*C(5,3)*3!=60种

故3-5之间方案数为136种,完美解释样例。

那么可以推出一般规律,设2种颜色的小岛数是a,b,它们之间建桥的方案数res。

那么2种颜色之间桥数可以从0到min(a,b).

则对于a,b,有

 

核心实现代码:

    int A,B,C;
    A=B=C=0;
    int res=0;
    for(int i=0;i<=min(a,b);i++)
    {
        A+=((comb(a,i)*comb(b,i))%MOD*F[i])%MOD;
        A%=MOD;
    }
    for(int i=0;i<=min(c,b);i++)
    {
        B+=((comb(c,i)*comb(b,i))%MOD*F[i])%MOD;
        B%=MOD;
    }
    for(int i=0;i<=min(a,c);i++)
    {
        C+=((comb(a,i)*comb(c,i))%MOD*F[i])%MOD;
        C%=MOD;
    }
    //cout<<A<<" "<<B<<" "<<C<<endl;
    res=A*B;
    res%=MOD;
    res*=C;
    res%=MOD;

本题代码如下:

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 5000 + 5;
const int MOD = 998244353;
unsigned long long F[N], Finv[N], inv[N];//F是阶乘,Finv是逆元的阶乘
void init(){
    inv[1] = 1;
    for(int i = 2; i < N; i ++){
        inv[i] = (MOD - MOD / i) * 1ll * inv[MOD % i] % MOD;
    }
    F[0] = Finv[0] = 1;
    for(int i = 1; i < N; i ++){
        F[i] = F[i-1] * 1ll * i % MOD;
        Finv[i] = Finv[i-1] * 1ll * inv[i] % MOD;
    }
}
unsigned long long comb(int n, int m){//comb(n, m)就是C(n, m)
    if(m < 0 || m > n) return 0;
    return F[n] * 1ll * Finv[n - m] % MOD * Finv[m] % MOD;
}
int main(){
    init();

    unsigned long long a,b,c;
    unsigned long long A,B,C;
    A=B=C=0;
    cin>>a>>b>>c;
    unsigned long long res=0;
    for(int i=0;i<=min(a,b);i++)
    {
        A+=((comb(a,i)*comb(b,i))%MOD*F[i])%MOD;
        A%=MOD;
    }
    for(int i=0;i<=min(c,b);i++)
    {
        B+=((comb(c,i)*comb(b,i))%MOD*F[i])%MOD;
        B%=MOD;
    }
    for(int i=0;i<=min(a,c);i++)
    {
        C+=((comb(a,i)*comb(c,i))%MOD*F[i])%MOD;
        C%=MOD;
    }
    //cout<<A<<" "<<B<<" "<<C<<endl;
    res=A*B;
    res%=MOD;
    res*=C;
    res%=MOD;
    cout<<res<<endl;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值