容斥原理(笔记)

在这里插入图片描述

对于三个圆, S1,S2,S3他们如上图所示,现在要求求他们所构成图形的面积,该如何去求?

这个问题很简单, S = S 1 + S 2 + S 3 − ( S 1 ∩ S 2 ) − ( S 1 ∩ S 3 ) − ( S 2 ∩ S 3 ) + ( S 1 ∩ S 2 ∩ S 3 ) S = S_1 + S_2 + S_3 - (S_1\cap S_2) - (S_1 \cap S_3) - (S_2 \cap S_3) + (S_1 \cap S_2 \cap S_3) S=S1+S2+S3(S1S2)(S1S3)(S2S3)+(S1S2S3)
如果换成四个呢?

经过猜想推广可以猜到四个的话就是
S = C 4 1 S ? − C 4 2 ( S ? ∩ S ? ? ) + C 4 3 ( S ? ∩ S ? ? ∩ S ? ? ? ) − C 4 4 ( S ? ∩ S ? ? ∩ S ? ? ? ∩ S ? ? ? ? ) S = C_4^1S_? - C_4^2(S_? \cap S_{??}) + C_4 ^3 (S_? \cap S_{??} \cap S_{???}) - C_4^4(S_? \cap S_{??} \cap S_{???} \cap S_{????}) S=C41S?C42(S?S??)+C43(S?S??S???)C44(S?S??S???S????)
其实用话来描述就是, C 4 1 C_4^1 C41个单个的面积 - C 4 2 C_4^2 C42两个相交的面积…很好理解

对于这样的计算,可以发现有正负之分,当组合数上选择的的数是奇数时该项为正,否则为负。

能被整除的数
思路:

把每个质数能整除的数都看成集合,之间相互有交集,现在其实要求的就是并集
对于1 - n之间的数,能被质数 m 1 m_1 m1整除的有 n m 1 \frac{n}{m_1} m1n个,能被 m 1 ∗ m 2 m_1 * m_2 m1m2整除的有 n m 1 ∗ m 2 \frac{n}{m_1*m_2} m1m2n个。

对于每个质数,枚举所有的组合,都可以看做选或者不选,很自然就可以想到二进制枚举,复杂度即 O ( m 2 ) O(m^2) O(m2)

#include<bits/stdc++.h>
#define ll long long
#define eps 1e-8
#define INF 0x3f3f3f3f
#define pb push_back
#define endl '\n'
#define IO ios::sync_with_stdio(false)
using namespace std;
const ll N = 21,M = 1 << N;
const ll mod = 1e9 + 7;
int n,m,p[25];
ll ans = 0;
int main(){
    cin >> n >> m;
    for(int i = 0; i < m; i++)
        cin >> p[i];
    for(int i = 1; i < (1 << m); i++){//二进制枚举
        ll t = 1,cnt = 0;
        for(int j = 0; j < m; j++){
            if((i >> j) & 1){
                cnt++;//统计选取的个数
                if(t * p[j] > n){//如果比n大则不合法
                    t = -1;
                    break;
                }
                t *= p[j];
            }
        }
    if(t != -1){//根据选取的个数进行计算
            if(cnt % 2)ans += n / t;
             else ans -= n / t;
        }
    }
    cout << ans << endl;
    return 0;
}
牛牛的棋盘
思路:

首先,考虑从n * m个格子中挑k个格子放棋子,即 C n ∗ m k C_{n*m}^k Cnmk种。
然后减去不符合条件的即,减去周围没有放棋子的情况,比如顶端没放有 C ( n − 1 ) ∗ m k C_{(n - 1) * m}^k C(n1)mk种,如此类推,可以发现就是一个容斥定理

typedef long long ll;
const ll mod = 1e9 + 7;
ll inv[1005], fec[1005];
class Solution {
public:
    
    ll qpow(ll a,ll b){
        ll res = 1;
        while(b){
            if(b&1)res = res * a % mod;
            b >>= 1;
            a = a * a % mod;
        }
        return res;
    }
    void init(){
        fec[0] = 1;
        for(int i = 1; i <= 1000; i++)
            fec[i] = fec[i - 1] * i % mod;
        inv[1000] = qpow(fec[1000], mod - 2);
        for(int i = 999; i >= 0; i--)
            inv[i] = inv[i + 1] * (i + 1) % mod;
    }
    ll C(ll n,ll m){
        return fec[n] * inv[n - m] % mod * inv[m] % mod; 
    }
    int solve(int n, int m, int k) {
        init();
        int ans = 0;
        for(int i = 0,N ,M,cnt; i < (1 << 4); i++){
            N = n,M = m,cnt = 0;
            for(int j = 0; j < 4; j++){
                if((i >> j) & 1){
                    if(j & 1)N--;
                    else M--;
                    cnt++;
                }
            }
            if(N * M < k)continue;
            if(cnt&1)ans = ans - C(N * M, k);
            else ans = ans + C(N * M, k);
        }
        return (ans % mod + mod) % mod;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值