Character Encoding hdu6397 (容斥原理)(组合数)

传送门:Character Encoding

题意比较简单:把k分成m份,每份都要小于n,有多少种分法,

听了dls的课,学了两种方法推式子。

一、生成函数:

用到了多项式展开、等差数列求和、泰勒展开等知识点。

还有一位群里的大佬分享的利用二项式定理推导的过程,大佬果然思如涌流  orz

二、容斥原理:

知识补充1:若x1,x2,.....xn均大于等于0,则x1+x2+...+xn=k的方案数是C(k+m-1,m-1)种(貌似紫书上有,记不太清了)。

知识补充2:若限制条件为n(即x1,x2....xn均小于n,假设有c个违反,则把k减掉c个n(相当于把c个超过n的数也变成大于等于0的),就可以套用知识1的公式了。

则最后的答案为sum( (-1)^c * C(m , c) * C(m-1+k-n*c , m-1) );

http://www.cnblogs.com/pkgunboat/p/9484230.html

可以先计算将k分成m份的方法,然后减去包含超出n的数的种类数,发现减去至少包含一个违反规则数字的C()时,

C(k-n+m-1,m-1)可以保证是不重复的,但乘以C(m,1)后,就会出现含有两个违反规则的数字的重复情况

例如

n=2 m=3  k=5

可以将 5-2  分的C(k-n+m-1,m-1)包含

【 0 2 1 】 ① 与【 2 0 1 】 ② 两种情况

但是乘以C(m,1)后就分别产生了

①中将2插入首位【2 2 1】

②中将2插入中间位【2 2 1】

的重复情况,就需要减去有至少有两种违反规则的情况

但是在减去至少有两个违反规则的情况时,又多减去了至少有三个违反规则的情况的个数,再加上至少有三个违反规则的数的情况,…………一直到小于0为止

上代码吧

#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353;
const int maxn = 1e7+7;
typedef long long ll;
ll fac[maxn];
ll power(ll a,ll b)
{
    ll ans=1;a%=mod;
    for(ll i=b;i;i>>=1,a=a*a%mod)
        if(i&1)ans=ans*a%mod;
    return ans;
}
ll C(ll n,ll m)
{
    if(m>n||m<0)return 0;
    ll s1=fac[n],s2=fac[n-m]*fac[m]%mod;
    return s1*power(s2,mod-2)%mod;
}

int solve(int n,int m,int k)
{
    int ans=C(k+m-1,m-1);
    for(int i=1;k-i*n>=0&&i<=m;i++) //一定要有=号,因为对于结果是0(即不可能分成m份的情况)
    {                               //的情况,要减去全是n的情况  
        if(i&1)   ans=(ans-C(m,i)*C(k-i*n+m-1,m-1)%mod+mod)%mod; //中间C()算出来要mod一次,因为ll型的怕溢出了
        else ans=(ans+C(m,i)*C(k-i*n+m-1,m-1)%mod)%mod;
    }
    return ans;
}

int main()
{
    fac[0]=1;
    for(int i=1;i<maxn;i++)
        fac[i]=fac[i-1]*i%mod;
    int casen;
    scanf("%d",&casen);
    while(casen--)
    {
        int n,m,k;
        scanf("%d%d%d",&n,&m,&k);
        int ans=solve(n,m,k);
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/sadsummerholiday/article/details/81747810
今日推荐