题目大意:有一圈石头分别标号0 ~ (m-1) 然后有n只青蛙,每只青蛙能且只能一次跳a_i 个石头 问所有能跳到的石头的编号和是多少
题解:
0 ~ (m-1) 这m个数和 m 的 gcd 有一些是相同的
把相同的gcd的数划分到一个集合里
这样就会有很多集合
这些集合互不相同 并且就是这m-1个数的一个划分
而且如果集合中的一个数是可以访问到的
那么这个集合里的所有数都是可以访问到的
那么怎么得到这个集合呢?
集合里最小的数和这个集合的数与m的gcd一定相等
或者说这个集合的数与m的gcd一定在这个集合里
并且这个数是m的因子
假设这个数为x 所以 任意 x_i 属于该集合有: gcd(xi,m)=x g c d ( x i , m ) = x
所以 gcd(xi/x,m/x)=1 g c d ( x i / x , m / x ) = 1
所以 xi/x x i / x 与 m互
所以 如果能把 ∑xi/x ∑ x i / x 的和求出来就好了
这里有一个结论是 欧拉函数 (ϕ(x)∗x)/2 ( ϕ ( x ) ∗ x ) / 2 是所有与x互质的数的和
所以对于一个集合 我们找到这个集合最小的数x
如果x能被 某个 gcd(ai,m) g c d ( a i , m ) 整除的话,就把当前集合的和加进去
ϕ(m/x)∗(m/x)2∗x=ϕ(m/x)∗m2
ϕ
(
m
/
x
)
∗
(
m
/
x
)
2
∗
x
=
ϕ
(
m
/
x
)
∗
m
2
那么我们怎么找到所有的x?
前面已经提到了
X是m的因数
枚举m的因数就ok了
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#include <set>
#include <cmath>
#include <map>
#include <vector>
using namespace std;
#define LL long long
#define lowbit(x) (x&(-x))
#define eps (1e-7)
#define twice(x) ((x)*(x))
const int maxn= 1e5+10;
int a[maxn];
int gcd(int x,int y){return x%y==0?y:gcd(y,x%y);}
LL phi(LL x)
{
LL p =x;
for(LL i= 2;i*i<=x;i++)
if(x%i==0)
{
p = p/i*(i-1);
while(x%i==0)
x/=i;
}
if(x!=1)p= p/x*(x-1);
return p;
}
int n,m;
bool check(int x)
{
for(int i=1;i<=n;i++)
if(x%a[i]==0)return 1;
return 0;
}
int main()
{
int T,cnt=0;
cin>>T;
while(T--)
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i]= gcd(a[i],m);
}
LL ans =0;
for(int i=1;i*i<=m;i++)
if(m%i==0)
{
if(check(i))ans+=phi(m/i)*m/2;
if(i*i==m||i==1)continue;
if(check(m/i))ans+=phi(i)*m/2;
}
printf("Case #%d: %lld\n",++cnt,ans);
}
}