题面
给定两个数a和m,在
[
0
,
m
)
[0,m)
[0,m)内寻找有多少个x使
g
c
d
(
a
,
m
)
=
g
c
d
(
a
+
x
,
m
)
gcd(a,m)=gcd(a+x,m)
gcd(a,m)=gcd(a+x,m)
1<=a<m<=1010
分析
询问了队友后得到了正确的建模思路。。。
可以先把问题简化,通过gcd(a,m)化简一波:
g
c
d
(
a
/
g
c
d
(
a
,
m
)
,
m
/
g
c
d
(
a
,
m
)
)
=
1
gcd(a/gcd(a,m),m/gcd(a,m))=1
gcd(a/gcd(a,m),m/gcd(a,m))=1
为表达简单,使
a
=
a
/
g
c
d
(
a
,
m
)
,
m
=
m
/
g
c
d
(
a
,
m
)
a=a/gcd(a,m),m=m/gcd(a,m)
a=a/gcd(a,m),m=m/gcd(a,m)
注意到此时问题变成了一个互质问题
在
[
0
,
m
)
[0,m)
[0,m)内寻找有多少个x使
1
=
g
c
d
(
a
+
x
,
m
)
1=gcd( a + x, m)
1=gcd(a+x,m)
(x的范围可能缩小了,但是删去的部分肯定不是答案,因为那部分x包含原来的a和m的公因子,不能使
g
c
d
(
a
+
x
,
m
)
=
1
gcd(a+x,m)=1
gcd(a+x,m)=1)
联想到欧几里得算法,可以将后面的部分化到最简:当a+x大于等于m时,
g
c
d
(
a
+
x
,
m
)
=
g
c
d
(
(
a
+
x
)
−
m
,
m
)
gcd(a+x,m)=gcd((a+x)-m,m)
gcd(a+x,m)=gcd((a+x)−m,m)
分类:
a+x<m时,
g
c
d
(
a
+
x
,
m
)
gcd(a+x,m)
gcd(a+x,m),其中
a
+
x
∈
[
a
,
m
)
a+x∈[a,m)
a+x∈[a,m)
a+x>=m时,
g
c
d
(
a
+
x
,
m
)
=
g
c
d
(
a
+
x
−
m
,
m
)
gcd(a+x,m)=gcd(a+x-m,m)
gcd(a+x,m)=gcd(a+x−m,m),其中
a
+
x
−
m
∈
[
0
,
a
)
a+x-m∈[0,a)
a+x−m∈[0,a)
由此,问题变成求 [ 0 , m ) [0,m) [0,m)内与m互质数的个数,即欧拉函数φ(m)
代码
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
ll gcd(ll a, ll b)
{
if (b == 0)return a;
return gcd(b, a % b);
}
int main()
{
ios::sync_with_stdio(false);
int t;
cin >> t;
ll a, m,fai=1,g;
for (int o = 0; o < t; o++)
{
cin >> a >> m;
g = gcd(a, m);
m /= g;
//求m/gcd(a,m)的欧拉函数
fai = m;
for (ll i = 2; i*i <= m; i++)
{
if (m % i == 0) {
fai = fai / i * (i - 1);
while (m % i == 0)m /= i;
}
}
if (m > 1)fai = fai / m * (m - 1);
cout << fai << endl;
}
return 0;
}