容斥原理(笔记)

在这里插入图片描述

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

这个问题很简单,S=S1+S2+S3(S1S2)(S1S3)(S2S3)+(S1S2S3)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=C41S?C42(S?S??)+C43(S?S??S???)C44(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_{????})
其实用话来描述就是,C41C_4^1个单个的面积 - C42C_4^2两个相交的面积…很好理解

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

能被整除的数

思路:

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

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

#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个格子放棋子,即CnmkC_{n*m}^k种。
然后减去不符合条件的即,减去周围没有放棋子的情况,比如顶端没放有C(n1)mkC_{(n - 1) * m}^k种,如此类推,可以发现就是一个容斥定理

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;
    }
};
展开阅读全文
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值