题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5514
容易得出一跳距离为x的青蛙在m个石头上形成的等差数列差值d=gcd(x,d),问题转化成在0~m-1区间上有一些d=m的因数(因为gcd(x,m)是m的因数)的等差数列螚覆盖多少个石头。
首先O(sqrt(m))处理出m的所有因数,如果某个因数是某个gcd值的倍数,则说明其一定至少出现一次,将其vis值置为1,之后用容斥定理,从小到大遍历至少会出现一次的因数,并且处理是它的倍数的因数,使其下次计算时不用计算这么多次,比较巧妙的容斥
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
long long fac[N],vis[N];
long long num[N];
int main()
{
int t;
int cas=1;
scanf("%d",&t);
while(t--)
{
memset(vis,0,sizeof vis);
memset(num,0,sizeof num);
long long n,m;
scanf("%I64d%I64d",&n,&m);
int tot=0;
for(int i=1;i*i<=m;i++)
{
if(m%i==0)
{
fac[++tot]=i;
if(i*i!=m)
fac[++tot]=m/i;
}
}
sort(fac+1,fac+tot+1);
long long x;
for(int i=1;i<=n;i++)
{
scanf("%I64d",&x);
x=__gcd(x,m);
for(int j=1;j<=tot;j++)
if(fac[j]%x==0)
vis[j]=1;
}
long long ans=0;
for(int i=1;i<=tot;i++)
{
if(vis[i]==num[i])
continue;
long long p=(m-1)/fac[i];
ans+=p*(p+1)/2*fac[i]*(vis[i]-num[i]);
for(int j=i+1;j<=tot;j++)
if(fac[j]%fac[i]==0)
num[j]+=(vis[i]-num[i]);
}
printf("Case #%d: %I64d\n",cas++,ans);
}
return 0;
}