容斥原理:
容斥原理(Inclusion-Exclusion Principle)是组合数学中的一个重要原理,用于计算多个集合的并集的大小。它通过考虑集合的交集来避免重复计数。
(可点击此处进行学习)
CF 547C Mike and Foam
题意:
有 q 次操作,每次输入一个 i ,如果 a[i] 没有被拿出来就将它拿出来,如果已经拿出来了就放回去,然后输出每次操作后,拿出来的数里有多少个对 (a[i], a[j]) 互质
思路:
我们可以将每个数的质因子分解出来,因为\(a[i]<=10^5\),并且,最小的\(7\)个质数相乘大于\(10^5\),所以每个数分解后最多\(6\)个因子,可以直接存下来
用一个数组\(cnt\),\(cnt[i]\)表示拿出来的数中有多少个\(i\)的倍数,每次对一个数进行操作时,枚举\(x\)的因子的组合,通过容斥原理就可以求出与\(x\)不互质的数的个数,再用\(cnt[1]\)减去就是与\(x\)互质的个数。所以就可以得到我们的公式:
\(\Delta \textrm{ans} = \textrm{cnt[1]}+(-1)^1\sum{cnt[p_i]}+(-1)^2\sum{cnt[p_i*p_j]}+\dots\)
具体实现过程见代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N = 200010;
const int MAXN = 500010;
typedef long long ll;
// 2 3 5 7 11 13 17
int a[N];
bool v[N];
ll ans;
vector<int> cnt(MAXN, 0), p[MAXN];
int n, q;void add(int x, int k){int sz = p[x].size();for(int i = 0;i < (1 << sz);i ++){int temp = 1;for(int j = 0;j < sz;j ++)if((i >> j) & 1) temp *= p[x][j];cnt[temp] += k;}
}
void query(int x, int k){ll res = 0;int sz = p[x].size();for(int i = 0;i < (1 << sz);i ++){int f = -1, temp = 1;for(int j = 0;j < sz;j ++){if((i >> j) & 1){temp *= p[x][j];f *= -1;}}res += f * cnt[temp];}ans -= res * k;
}
int main(){ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);for(int i = 2;i < MAXN;i ++)if(p[i].empty())for(int j = i;j < MAXN;j += i) p[j].push_back(i);cin >> n >> q;for(int i = 1;i <= n;i ++) cin >> a[i];while(q --){int op;cin >> op;if(!v[op]){v[op] = true;query(a[op], 1);add(a[op], 1);}else{v[op] = false;add(a[op], -1);query(a[op], -1);}cout << ans << '\n';}return 0;
}
CF 449D Jzzhu and Numbers
题意:
从数列中取几个数,要求将它们“&”后得到0,问有多少种取法
思路(来处:Azazel):
(蒟蒻不太会写,原文更加详细)
类似上面的枚举因子,这个题我们枚举一个数在二进制下为 0 的位数,再用上一个叫高维前缀和的
我们不好直接找出符合答案要求的,但是可以找出不符合要求的
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1000000007;const int N = 2000010;
ll p[N];
ll ans = 0;
ll g[N];
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;
}
ll cnt_1(ll x){ll res = 0;while(x){if(x & 1) res++;x >>= 1;}return res;
}
ll mul(ll a,ll b){return a*b % mod;}
int main(){ios::sync_with_stdio(false);cin.tie(nullptr);int n;cin >> n;for(int i = 1;i <= n;i ++){int x;cin >> x;p[x] ++;}for(int j = 0;j < 20;j ++)for(int i = (1 << 20) - 1;~i;i --)if(!((i >> j) & 1)) p[i] += p[i | (1 << j)];for(int i = 0;i < (1 << 20);i ++) g[i] = (qpow(2, p[i]) - 1) % mod;for(int i = 0;i < (1 << 20);i ++){// --- 这一步取余的操作非常容易忽视 --ans = ((ans + mul(g[i], cnt_1(i) & 1 ? -1 : 1)) % mod + mod) % mod;}cout << ans;
}