容斥原理解决某个区间[1,n]闭区间与m互质数数量问题

时间:2021-06-14 17:06:37

首先贴出代码(闭区间[1,n]范围内和m互质的数)

代码:

int solve(II n,II m){
vector
<II>p;
for(II i=2;i*i<=m;i++){
if(m%i==0){
p.push_back(i);
while(m%i==0) m/=i;
}
}
if(m>1) p.push_back(m);
II sz
=p.size();
LL sum
=0;
for(II i=1;i<(1<<sz);i++){
II ct
=0;
LL mul
=1;
for(II j=0;j<sz;j++){
if(i&(1<<j)){
ct
++;
mul
*=p[j];
}
}
if(ct&1) sum+=n/mul;
else sum-=n/mul;
}
return n-sum;
}

这里解释一下原理:首先假设m有x个不同的质因子,那么可以组成的因子数就是2^x-1种,然后10^18以内所有的数的质因子个数不会超过15个,所以2^15次方暴力枚举所有情况这个复杂度还是可取的。我们假设p1,p2,p3都是m的质因子,假设当前枚举的因子是p1*p2*p3那么n以内可以整除p1*p2*p3的数量就是n/(p1*p2*p3),但是这里考虑到一个会重复问题就是拥有奇数个质因数的因子的在n以内可以整除的数量已经包含了偶数个数量,但是偶数个的并不包含奇数个的,所以我们枚举的时候需要奇加偶减,这样我们算出来的ans是n以内和m不互质的数的数量,那么和m互质的数量就是n-ans。

贴上一些可以用这个方法解决的练习题:

HDU1695:这个题好像正解使用什么莫比乌斯反演,题目的意思是求[1,a],[1,b]有多少对数GCD(x,y)==k,但是(x,y)和(y,x)算一对,有一个定理如果GCD(x,y)==k ,则GCD(x/k,y/k)==1那么现在问题转化为求[1,a/k],[1,b/k]以内互质的数有多少对,现在我们要取minn=min(a/k,b/k),我们可以先求minn的欧拉函数的前缀和,然后再求minn+1到max(a/k,b/k)的数在[1,max(a/k.b/k)]以内所有与它互质的数的数量,然后把所有的加起来就可以了。

代码:

容斥原理解决某个区间[1,n]闭区间与m互质数数量问题容斥原理解决某个区间[1,n]闭区间与m互质数数量问题
 1 //Author: xiaowuga
2 #include <bits/stdc++.h>
3 using namespace std;
4 #define inf 0x3f3f3f3f
5 #define MAX INT_MAX
6 #define mem(s,ch) memset(s,ch,sizeof(s))
7 const long long N=100000;
8 const long long mod=1e9+7;
9 typedef long long LL;
10 typedef int II;
11 typedef unsigned long long ull;
12 #define nc cout<<"nc"<<endl
13 #define endl "\n"
14 vector<LL>Euler;
15 void init_Euler(II n){
16 Euler.resize(n+1);
17 Euler[0]=0;
18 Euler[1]=1;
19 for(LL i=2;i<n;i++) Euler[i]=i;
20 for(LL i=2;i<n;i++){
21 if(Euler[i]==i){
22 for(LL j=i;j<n;j+=i)
23 Euler[j]=Euler[j]/i*(i-1);//先进行除法防止溢出
24 }
25 }
26 for(II i=1;i<n;i++){
27 Euler[i]+=Euler[i-1];
28 }
29 }
30 int solve(II n,II m){
31 vector<II>p;
32 for(II i=2;i*i<=m;i++){
33 if(m%i==0){
34 p.push_back(i);
35 while(m%i==0) m/=i;
36 }
37 }
38 if(m>1) p.push_back(m);
39 II sz=p.size();
40 LL sum=0;
41 for(II i=1;i<(1<<sz);i++){
42 II ct=0;
43 LL mul=1;
44 for(II j=0;j<sz;j++){
45 if(i&(1<<j)){
46 ct++;
47 mul*=p[j];
48 }
49 }
50 if(ct&1) sum+=n/mul;
51 else sum-=n/mul;
52 }
53 return n-sum;
54 }
55 int main() {
56 ios::sync_with_stdio(false);cin.tie(0);
57 II a,b,c,d,k;
58 II T,ca=0;
59 init_Euler(100000+1000);
60 cin>>T;
61 while(T--){
62 cin>>a>>b>>c>>d>>k;
63 if(k==0||k>b||k>d){
64 cout<<"Case "<<++ca<<": "<<0<<endl;
65 continue;
66 }
67 if(b>d) swap(b,d);
68 b/=k;d/=k;
69 LL ans=Euler[b];
70 for(II i=b+1;i<=d;i++){
71 ans+=solve(b,i);
72 }
73 cout<<"Case "<<++ca<<": "<<ans<<endl;
74 }
75 return 0;
76 }
View Code

POJ2773:这个题求第n个与k互质的数。

1.一个简单的思路就是如果[1,k]中有x个与k互质的数,那么[k+1,2*k]中也有x个,每个k个一个循环都会出现x个数与k互质,而且加入y<k且y与k互质,则t*k+y也与k互质。那么我们第一个方法可以暴力算出比k小的数里面所有与k互质的数都是多少复杂度是klog(k),然后看一下n是k第几轮循环里面。然后直接确定这个数多少。

2.二分+验证的思想,首先一个数越大,那么他包含的与k互质的数一定越多对吧?这个性质不就是单调性吗?任何满足单调性的问题我们都可以用二分来解决,所以我们可以用二分,加验证的思想找到和第n个k互质的数是多少

代码:

容斥原理解决某个区间[1,n]闭区间与m互质数数量问题容斥原理解决某个区间[1,n]闭区间与m互质数数量问题
 1 //Author: xiaowuga
2 #include<iostream>
3 #include<vector>
4 using namespace std;
5 #define inf 0x3f3f3f3f
6 #define MAX INT_MAX
7 #define mem(s,ch) memset(s,ch,sizeof(s))
8 const long long N=100000;
9 const long long mod=1e9+7;
10 typedef long long LL;
11 typedef int II;
12 typedef unsigned long long ull;
13 #define nc cout<<"nc"<<endl
14 #define endl "\n"
15 LL solve(II r,LL n){
16 vector<int>p;
17 for(II i=2;i*i<=r;i++){
18 if(r%i==0){
19 p.push_back(i);
20 while(r%i==0) r/=i;
21 }
22 }
23 if(r>1) p.push_back(r);
24 LL sum=0;
25 for(LL i=1;i<(1<<p.size());i++){
26 LL d=1,ct=0;
27 for(II j=0;j<p.size();j++){
28 if(i&(1<<j)){
29 ct++;
30 d*=p[j];
31 }
32 }
33 if(ct%2) sum+=n/d;
34 else sum-=n/d;
35 }
36 return n-sum;
37 }
38 int main() {
39 ios::sync_with_stdio(false);cin.tie(0);
40 II n,k;
41 while(cin>>n>>k){
42 LL l=1,r=inf*2;
43 LL ans=1;
44 while(l<r){
45 LL m=l+(r-l)/2;
46 LL t=solve(n,m);
47 if(t>=k){
48 if(t==k) ans=m;
49 r=m;
50 }
51 else{
52 l=m+1;
53 }
54 }
55 cout<<ans<<endl;
56 }
57 return 0;
58 }
View Code