容斥原理


//
容斥原理 //时间复杂度O(2^n-1) #include<bits/stdc++.h> #define int long long using namespace std; const int N=2e6+10; int n,m,res,p[N]; signed main() { cin>>n>>m; for(int i=0;i<m;i++) cin>>p[i]; for(int i=1;i<1<<m;i++){//枚举2^n-1次,因为有这些种可能 int cnt=0,t=1; for(int j=0;j<m;j++){ if(i>>j&1){ cnt++; t*=p[j]; if(t>n) break; } } if(cnt%2) res+=n/t; //如果是奇数,就加上 else res-=n/t; } cout<<res<<endl; return 0; }

 

 

 **基本概念:**

- |U|:所有情况的方案数
- Si:满足条件 i 的方案数
- ⋃:集合的并运算(表示满足任一条件的方案数)
- ⋂:集合的交运算(表示全部条件都满足的方案数)

**容斥原理公式:**

1. 求至少满足一个条件的方案数:

\[|\bigcup_{i=1}^{n} S_i| = \sum_{m=1}^{n} (-1)^{m-1} \sum_{1 \leq i_1 < i_2 < \ldots < i_m \leq n} |\bigcap_{j=1}^{m} S_{i_j}|\]

这个公式用于计算至少满足一个条件的方案数,其中 \(m\) 表示满足条件的条件数目,\(S_{i_j}\) 表示满足第 \(i_j\) 个条件的方案集合。

2. 求恰好满足所有条件的方案数:

\[|\bigcap_{i=1}^{n} S_i| = |U| - |\bigcup_{i=1}^{n} S_i|\]

这个公式用于计算恰好满足所有条件的方案数。

容斥的核心思想是对“至少(至多)”和“恰好(一般是)”之间的转换,通过交和并的运算来得到不同情况下的方案数。

//分特产
//https://www.luogu.com.cn/problem/P5505

//假设此时有i个人得不到第j个特产,则n-i个人分a[j]个特产,根据插板法可以得到
//方案数为 C(a[j]+n-i-1,n-i-1),而此时又要选择i个人,所以有C(n,i)个方法,根据乘法原理
//再使用容斥原理删除一些不合法的状态即可
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5000,mod=1e9+7;
int n,m,fac[N],_fac[N],k,a[N],res;
int qpow(int a,int k)
{
    int res=1;
    while(k){
        if(k&1) res=res*a%mod;
        a=a*a%mod,k/=2;
    }
    return res;
}
int C(int a,int b)
{
    return fac[a]*_fac[b]%mod*_fac[a-b]%mod;
}
signed main()
{
    cin>>n>>m;
    fac[0]=_fac[0]=1,k=1;
    for(int i=1;i<N;i++) fac[i]=fac[i-1]*i%mod;
    _fac[N-1]=qpow(fac[N-1],mod-2);
    for(int i=N-2;i;i--) _fac[i]=(i+1)*_fac[i+1]%mod;
    for(int i=1;i<=m;i++) cin>>a[i];
    for(int i=0;i<n;i++,k=-k){
        int cnt=1;
        for(int j=1;j<=m;j++) cnt=cnt*C(a[j]+n-i-1,n-i-1)%mod;
        res=(res+C(n,i)*k%mod*cnt%mod+mod)%mod;
    }
    cout<<res;
    return 0;
}

 

一个 n × m 的棋盘,用 c 种颜色染色,求满足条件的方案数:

- 棋盘的每一个小方格既可以染色(染成 c 种颜色中的一种),也可以不染色。
- 棋盘的每一行至少有一个小方格被染色。
- 棋盘的每一列至少有一个小方格被染色。
- 每种颜色都在棋盘上出现至少一次。

 

 

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1000,mod=1e9+7;
int n,m,fac[N],_fac[N],num,res,k,c;
int f[N];
int qpow(int a,int k)
{
    int num=1;
    while(k){
        if(k&1) num=num*a%mod;
        a=a*a%mod,k/=2;
    }
    return num;
}
int C(int a,int b)
{
    return fac[a]*_fac[b]%mod*_fac[a-b]%mod;
}
signed main()
{
    cin>>n>>m>>c;
    fac[0]=_fac[0]=1;
    for(int i=1;i<N;i++) fac[i]=i*fac[i-1]%mod;
    _fac[N-1]=qpow(fac[N-1],mod-2);                                         
    for(int i=N-2;i;i--) _fac[i]=(i+1)*_fac[i+1]%mod;
    for(int i=1;i<=c;i++){
        int cnt=0;
        for(int k=1,q=1;k<=m;k++,q=-q)
            cnt=(cnt+C(m,k)%mod*qpow(qpow(i+1,m-k)-1,n)%mod*q+mod)%mod;
        f[i]=(qpow(qpow(i+1,m)-1,n)%mod-cnt+mod)%mod;
    }
    int num=0;
    for(int i=1,q=1;i<=c;i++,q=-q)
        num=(num+(C(c,i)*f[c-i]%mod*q+mod)%mod)%mod;
    res=(f[c]-num+mod)%mod;
    cout<<res<<endl;
    return 0;
}

 

posted @ 2023-08-30 12:21  o-Sakurajimamai-o  阅读(45)  评论(0编辑  收藏  举报
-- --