codeforces gym 102192

题意:解方程: x1 + x2 +x3+x4+x5 + ...+xm = k( xi < n && xi > 0  )

坑点:这里取模犯了两个错误,首先,if( x > mod ) x-=mod;是错误的,正确的应该是含有等号的。其次,传入的参数一定保证取模之后再传入,否则加上一个mod是不够的。

思路1:容斥原理 先算没有上限的,再用容斥原理减去所有不合格的。( 具体来时就是,减去至少有一段大于等于n的所有可能情况数,加上所有至少有两段大于等于n的情况数。。。 )PS:注意是所有,这点很重要。

这题我一开始犯了一个愚蠢的错误就是去枚举不合格的那部分的具体情况数,实际上只需要将总数减去 不合格段数*n,剩下的直接隔板法就可以了。

#include <bits/stdc++.h>
typedef long long LL;
typedef long long ll;
const int maxn =1002000;
const ll oo=998244353;
LL ad( LL x,LL y ){
    x += y;
    if(x>=oo ) x-=oo;
    if( x<0 ) x+= oo;
    return x;
}
ll frac[maxn], carf[maxn];

ll inv(ll x){
    if (x==1) return 1;
    else return (oo-oo/x)*inv(oo%x)%oo;
}
void init(){
    frac[0]=1;
    for (int i=1;i<=1000000;i++)
        frac[i]=frac[i-1]*i%oo;
    carf[1000000]=inv(frac[1000000]);
    for (int i=1000000;i>=1;i--)
        carf[i-1]=carf[i]*i%oo;
}

ll C(int n, int m){
    if (m>n) return 0;
    else return frac[n]*carf[n-m]%oo*carf[m]%oo;
}
int main(){
    init();
    int T,n,m,k;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&n,&m,&k);
        LL ans = C( k+m-1,m-1 );
        int  p =1;
        int h = k/n;
        for( int i = 1;i <= h;i++ ){
            p*=-1;
            LL v = 1LL*p*C(m,i)%oo*C( k-i*n+m-1,m-1 )%oo;
            ans = ad( ans,v );
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

思路2:生成函数裸题,用到广义二项式展开定理进行求解

#include <bits/stdc++.h>
typedef long long LL;
typedef long long ll;
#define maxn 1002000
const ll oo= 998244353 ;
ll frac[maxn], carf[maxn];
ll inv(ll x){
    if (x==1) return 1;
    else return (oo-oo/x)*inv(oo%x)%oo;
}
void init(){
    frac[0]=1;
    for (int i=1;i<=1000000;i++)
        frac[i]=frac[i-1]*i%oo;
    carf[1000000]=inv(frac[1000000]);
    for (int i=1000000;i>=1;i--)
        carf[i-1]=carf[i]*i%oo;
}

ll C(int n, int m){
    if (m>n) return 0;
    else return frac[n]*carf[n-m]%oo*carf[m]%oo;
}
int ad( LL x,LL y ){
    x += y;
    if( x>=oo ) x -= oo;
    if( x < 0 ) x+= oo;
    return x;
}
int main(){
    int T,n,m,k;
    init();
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&n,&m,&k);
        LL ans = 0;
        for( int k2 = 0;k2 <= m;k2++ ){
            if( n*k2 > k ) break;
            int k1 = k-k2*n;
            LL v = C( m+k1-1,k1 )*C(m,k2)*(k2&1?-1:1);
            ans = ad( ans,C( m+k1-1,k1 )*C(m,k2)%oo*(k2&1?-1:1) );
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值