题意
有m个环形石子,编号为0...m-1,围成一个圆,距离为1,有n只青蛙,从0位置跳,每次跳跃的长度为ai,求它们所跳过的石子标号的和。
思路
设
,那么第i个青蛙跳的标号的公差为gcdx,且小于m。多只青蛙之间会有重复计算,所以要用到容斥原理。其中
一定是m的因子。
先求出来m的因子,然后求出每一个因子的贡献,因为青蛙走过的路径一定是以m的因子为起点的。
以因子x为起点,那么答案就是
,化简为
。
用vis代表应该的贡献,用num表示已经有的贡献。
那么
即为贡献,不断加加减减,最后得到答案。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn 10000+66
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
#define mod 1000000007
#define INF 0x3f3f3f3f
ll gcd(ll a,ll b)
{
return b==0?a:gcd(b,a%b);
}
ll vis[maxn],num[maxn];
ll a[maxn];
int main()
{
int t;
scanf("%d",&t);
int n,m;
rep(t1,1,t)
{
scanf("%d %d",&n,&m);
rep(i,1,10000+62)
{
num[i]=0;
vis[i]=0;
a[i]=0;
}
int cnt=0;
for(int i=1; i*i<=m; i++)
{
if(m%i==0)
{
a[++cnt]=i;
if(i*i!=m)
{
a[++cnt]=m/i;
// cout<<m/i<<"--"<<endl;
}
//cout<<i<<"---"<<endl;
}
}
sort(a+1,a+cnt+1);
rep(i,1,n)
{
int x;
scanf("%d",&x);
int y=gcd(x,m);
rep(j,1,cnt)
{
if(a[j]%y==0)
{
vis[j]=1;
}
}
}
vis[cnt]=0;
ll ans=0;
rep(i,1,cnt)
{
if(num[i]!=vis[i])
{
//cout<<i<<"--"<<endl;
ll x=m/a[i];
ans+=(ll)((x-1)/2*a[i]*(vis[i]-num[i]));
rep(j,i+1,cnt)
{
if(a[j]%a[i]==0)
{
num[j]+=(vis[i]-num[i]);
}
}
}
}
printf("Case #%d: %I64d\n",t1,ans);
}
}