【组合数学】容斥

概念

先容纳全部的情况,然后排斥掉不合法的情况。

对于集合求并问题,我们可以得到:

\[\left| \bigcup_{i=1}^{n}P_i \right| =\sum_{S\subseteq[1,2,\ldots,n]} \left( -1 \right)^{\left| S \right|-1} \left| \bigcap_{s\in S} P_s \right| \]

例题1

不定方程:\(x_1+x_2+\dots+x_n=S\),每个数都是非负整数,对于每个 \(x_i\),有限制 \(x_i\leq k\),计算不定方程的解数。\(n\leq 10^5,k\leq 10^5\)

枚举至少有几个大于 \(K\)\(S\)就变成了 \(S-i*(k+1)\times i\) 没有限制的问题,插板法即可。

\[ans=\sum_{i=0}^{i\times k\leq S} (-1)^{i} \cdot C_{n}^{i} \cdot C_{S-i\times (k+1)+n-1}^{n-1} \]

为什么要 \((-1)^i\) 呢?

因为 \(i=1\) 时存在 \((0,3,1)\)\((3,0,1)\) 都变成 \((3,3,1)\),重复计算了,要加上 \(i=2\) 时存在 \((0,0,1)\) 变成 \((3,3,1)\) 的情况。

例题2

\(n\) 游戏局,这个人赢 \(m\) 局,最长连胜是 \(k\) 局,如果我们用 \(1\) 表示胜利,用 \(0\) 表示失败,战绩可以看作长度为 \(n\)\(01\) 字符串,两个战绩不同当且仅当两个字符串不同。

限制在赢上,将输的局看作分隔符,赢的局看作变量,此时题目可看作不定方程 \(x_1+x_2+\dots+x_{n-m+1}=m,\max\{{x_i}\}=k\)

因为必须要有至少一局连胜 \(k\) 局,那么将 \(x_i\leq k\) 的方案数减去 \(x_i\leq k-1\) 的方案数,求法就可以看例题1了。

#include<bits/stdc++.h>
#define ll long long
#define int ll
using namespace std;
const int N=2e5+10;
const int mod=998244353;
int fac[N],inv[N];

int choose(int n,int m){
    if(m<0||n<m) return 0;
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}

int quick(int a,int k){
    int base=a%mod;
    int ans=1;
    while(k){
        if(k&1){
            ans=(ans*base)%mod;
        }
        base=(base*base)%mod;
        k>>=1;
    }
    return ans;
}



int n,m,k,s;

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(nullptr);   
    fac[0]=1;
    for(int i=1;i<=100005;i++){
        fac[i]=fac[i-1]*i%mod;
    }
    inv[100005]=quick(fac[100005],mod-2);
    for(int i=100004;i>=0;i--){
        inv[i]=inv[i+1]*(i+1)%mod;
    }
    cin>>n>>s>>k;
    if(!(n>=s&&s>=k)){
        cout<<0;
        return 0;
    }
    if(k==0){
        if(s==0){
            cout<<1;
        }
        else{
            cout<<0;
        }
        return 0;
    }
    n-=s;
    n++;
    int ans=0;
    for(int i=0;i*k<=s;i++){
        int x=choose(n,i)*choose(s-i*(k+1)+n-1,n-1)%mod;
        int y=choose(n,i)*choose(s-i*(k+1-1)+n-1,n-1)%mod;
        if(i&1){
            ans+=y-x;
        }
        else{
            ans+=x-y;
        }
        ans=(ans%mod+mod)%mod;
    }
    cout<<ans;
    return 0;
}

\[C_{n(k+1)-s-1}^{n-1} \]

posted @ 2025-07-20 21:08  sad_lin  阅读(9)  评论(0)    收藏  举报