容斥原理
今天学习了容斥原理
上述为容斥原理的公式,推导建议百度。我们发现,当我们要求一个集合的并集的时候,可以很好的利用容斥原理来求解。那么我们可以得到一些很好的性质。
注意到
- 偶数个集合的交集前为加号,而奇数个集合交集前面为减号。
- 项的个数为2^k,原理为n个集合的排列组合
那么我们可以解决如下问题
HDU-4135 : https://vjudge.net/problem/HDU-4135
1 到 n 里面有多少个和 m 互质的数?
利用容斥原理的思想,可以先求 1 到 n 中有多少数和m不互质,我们发现,一个数和m不互质,那么它一定不是m的质因子的倍数,那么设m有质因子 p1 p2 … pn 那么对于质因子 pi 集合中就有 n/pi 个和 m 不互质的数,那么对应了n 个集合那么 pi pj 的交集里就有 n/(pi*pj) 个数 然后以此类推。
#include <bits/stdc++.h>
using namespace std;
long long solve(long long m,int n){
vector<int> v;
for(int i=2; i*i<=n; i++){
if (n % i == 0){
v.push_back(i);
while(n % i == 0) n/= i;
}
}
if (n > 1) v.push_back(n);
long long sum = 0;
for(int i=1; i< (1 << v.size()); i++){
int bit = 0; long long mul = 1;
for(int j=0; j<v.size(); j++){
if (i & (1 << j)){
bit++;
mul *= v[j];
}
}
if (bit & 1) sum += m/mul;
else sum -= m/mul;
}
return m - sum;
}
int main(){
int T,n;
long long a,b;
scanf("%d",&T);
for(int i=1; i<=T; i++){
scanf("%lld%lld%d",&a,&b,&n);
printf("Case #%d: %lld\n",i,solve(b,n) - solve(a-1,n));
}
return 0;
}
ACM-ICPC 2018 沈阳赛区网络预赛 G. Spare Tire
打表发现 an 的通项公式为 n*(n+1)
那么 Sn 的通项公式为 n*(n+1)*(2*n+1)/6
那么对于1 到 n 内和 m 互素的数我们可以用上述的容斥原理来求解
例如 m 素数因子 pi 那么对于答案的贡献就是范围内的含有pi因子的数的和
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
long long inv[100];
void init(int n){
inv[1] = 1;
for(int i = 2; i <=n; i ++)
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
}
long long cal(long long x,long long N){
long long n = N/x;
long long sum = n*(n+1)%mod*(2*n+1)%mod*inv[6]%mod*x % mod * x % mod + n*(n+1)%mod*inv[2] % mod * x%mod;
return sum % mod;
}
long long solve(long long n,long long m){
vector<int> v;
for(int i=2; i*i<=m; i++){
if (m % i == 0){
v.push_back(i);
while(m % i == 0) m/= i;
}
}
if (m > 1) v.push_back(m);
long long sum = 0;
for(int i=1; i< (1 << v.size()); i++){
int bit = 0; long long mul = 1;
for(int j=0; j<v.size(); j++){
if (i & (1 << j)){
bit++;
mul *= v[j];
}
if (bit & 1) {
sum = (sum + cal(mul,n)) % mod;
}else{
sum = (sum + mod - cal(mul,n)) % mod;
}
}
return sum;
}
int main(){
init(100);
long long n,m;
while(scanf("%lld%lld",&n,&m) == 2){
long long sum1 = cal(1,n);
printf("%lld\n",(sum1+mod-solve(n, m)) % mod);
}
return 0;
}