链接: D. Same GCDs
题意:
求 [ 0 , m),有多少个 x 满足 gcd(a,m)=gcd(a+x,m);
思路:
- 设 a,m的 gcd 为 gc,要使 gcd(a+x,m)=gc, 那么 gcd( (a+x)/gc,m/gc)=1 , x必须为 gc的倍数。那么问题就变成了 有多少个 k 满足 gcd(a/gc+k,m/gc)=1,k的范围为 [0,m/gc),那么就是求 [a/gc,a/gc+m/gc) 中有多少个数与 m/gc 互质。到这里就和欧拉函数很相似了 ,这里是求 [l , l+r) 与 r 互质的数的个数,欧拉函数求的是 小于 r 且与 r 互质的数。其实这两者是相等的(我太菜了,证不出为啥相等,打表发现是相等的。
- 补一个写法,在得到需要求的是 [ l , l+r ]中与 r 互质的数的个数后,可以由容斥求得于它不互质的数的个数 (可参考 hdu 4135),就可以不用欧拉函数了。
#include<iostream>
#include<cstdio>
#include<map>
#include<math.h>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
ll gcd(ll a, ll b){
if(b==0) return a;
return gcd(b,a%b);
}
int main (){
int T;
cin>>T;
while(T--){
ll a,b,ans1=0,ans2=0;
scanf("%lld%lld",&a,&b);
ll x=gcd(a,b);
a/=x,b/=x;
ll l=ans1=a-1,r=ans2=a+b-1;
for(ll i=2;i*i<=b;i++){
if(b%i==0){
ans1=ans1/i*(i-1);
ans2=ans2/i*(i-1);
while(b%i==0) b/=i;
}
}
if(b!=1){
ans1=ans1/b*(b-1);
ans2=ans2/b*(b-1);
}
printf ("%lld\n",ans2-ans1);
}
}
容斥写法:
#include<iostream>
#include<cstdio>
#include<map>
#include<math.h>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
ll gcd(ll a, ll b){
if(b==0) return a;
return gcd(b,a%b);
}
ll a[maxn];
ll solve(ll n,ll x){
ll k=0,ans=0;
for(ll i=2;i*i<=x;i++){
if(x%i==0){
a[k++]=i;
while(x%i==0) x/=i;
}
}
if(x!=1) a[k++]=x;
for(ll i=1;i<(1<<k);i++){
ll bits=0,sum=1;
for(int j=0;j<k;j++){
if((i>>j)&1){
bits++; sum*=a[j];
}
}
if(bits%2==1) ans+=n/sum;
else ans-=n/sum;
}
return ans;
}
int main (){
int T;
cin>>T;
while(T--){
ll a,b,ans1=0,ans2=0,ans=0;
scanf("%lld%lld",&a,&b);
ll x=gcd(a,b);
a/=x,b/=x;
ll l=ans1=a-1,r=ans2=a+b-1;
ans=b-solve(r,b)+solve(l,b);
printf ("%lld\n",ans);
}
}
//求 [l ,l+b] 区间与 b 互质的数