m个石头围成一圈,一群青蛙从0开始跳,第i个青蛙每步跳ai距离,求所有能被跳到的石头的编号之和。
容易推出石头x被第i只青蛙跳到的充要条件是,显然这与下面的命题是等价的:
石头x被跳到的充要条件是存在一个i,使得。
gcd(x, m)显然是m的因数,那么我们就可以枚举m的因数。对于m的一个因数k,如果存在一个i使得k能被ai整除,则此时它产生的贡献就是:
实际上上面也就是小于m/k且与m/k互质的数之和,有这样一个结论:小于x且与x互质的数之和为。上式即为:
枚举m的因数计算即可。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e4+5;
int t, n, no;
ll m, x, num[maxn];
ll eul(ll x)
{
ll ans = x;
for(ll i = 2;i*i <= x;i++)
{
if(x % i == 0)
{
ans -= ans/i;
while(x % i == 0)
x /= i;
}
}
if(x > 1) ans -= ans/x;
return ans;
}
bool check(ll x)
{
for(int i = 0;i < no;i++)
{
if(x % num[i] == 0)
return true;
}
return false;
}
ll gcd(ll a, ll b)
{
if(b == 0) return a;
return gcd(b, a % b);
}
int main()
{
scanf("%d", &t);
int kase = 0;
while(t--)
{
scanf("%d%lld", &n, &m);
no = 0;
for(int i = 1;i <= n;i++)
{
scanf("%lld", &x);
num[no++] = gcd(x, m);
}
sort(num, num + no);
no = unique(num, num + no) - num;
ll ans = 0;
for(ll i = 1;i*i <= m;i++)
{
if(m % i != 0) continue;
if(check(i)) ans += eul(m/i);
if(i == 1 || i*i == m) continue;
if(check(m/i)) ans += eul(i);
}
ans = ans*m/2;
printf("Case #%d: %lld\n", ++kase, ans);
}
return 0;
}