HDU6397 Character Encoding (2018多校第八场1001) (组合数学+容斥原理+逆元)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=6397

题意:
m个桶,放k个小球,每个桶只能放0~n-1个球,求方案数

分析:
(1)由隔板法可知:
若没有少于n-1个的限制,则方案数为:C(k+m-1,m-1);
其实等价于,x1+x2+…+xm=k,(xi>=0)的解数

(2)接下来容斥:
考虑有i个桶违反了规定,放了>=n个;
此时对等式两边同时减去i个n,变成:
x1’+x2’+…+xm’=k-i*n,xj’=(xj < n? xj : xj-n)
这样就把违反规定的那i个桶的限制条件xj>=n变成了xj’>=0;
由(1)只,这个解的数量为C(k-i*n+m-1,m-1)
容斥系数为(-1)^i

注意预处理阶乘及其逆元

代码:

#include 
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int tmax=2e5+5;
ll ans,jie[tmax],rev[tmax];
int n,m,k;
ll quick_pow(ll x)
{
    ll k=mod-2,sum=1;
    while(k>0)
    {
        if(k&1) sum=sum*x%mod;
        x=x*x%mod;
        k>>=1;
    }
    return sum;
}
void init()
{
    jie[0]=rev[0]=1;
    for(int i=1;i<=200000;i++)
    {
        jie[i]=jie[i-1]*i%mod;
        rev[i]=quick_pow(jie[i]);
    }
    return;
}
int main()
{
    int T,i,tk;
    cin>>T;
    init();
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&k);
        ans=jie[k+m-1]*rev[m-1]%mod*rev[k]%mod;
        for(i=1;i<=m;i++)
        {
            tk=k-1ll*i*n;
            if(tk<0) break;
            if(i&1) ans=((ans-jie[m]*rev[i]%mod*rev[m-i]%mod*jie[tk+m-1]%mod*rev[m-1]%mod*rev[tk]%mod)%mod+mod)%mod;
            else ans=(ans+jie[m]*rev[i]%mod*rev[m-i]%mod*jie[tk+m-1]%mod*rev[m-1]%mod*rev[tk]%mod)%mod;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(同余,数论,容斥原理)