题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5514
题意:有一堆青蛙,一开始都在0点,然后有一堆圈成一圈的石子,石子的编号是从0~m-1。
青蛙只能顺时针跳,第i只青蛙可以一次跳a[i]格,然后所有青蛙都这样一直跳下去然后问你,这些青蛙踩过的石子的编号和是多少?
题解:
首先,对于第i只青蛙,他跳过的格子,一定是k*gcd(a[i],m)这种的
如果m小一点,我们就可以直接暴力了
当时m太大了,我们就分解m的因数之后,对于每个因数做暴力就好了
每个因数T的贡献是 for(int i=1;i<=M/T;i++)ans += M*i;
然后优化一下就好了,对于部分加多了的因数,我们后面用容斥搞一搞就行了
#include<iostream>
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<math.h>
using namespace std;
#define maxn 10005
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
//每个青蛙,可以跳到gcd(m,a[i])*k的位置
int ppp[maxn];
int num[maxn],vis[maxn];
int main()
{
int tt;scanf("%d",&tt);
for(int cas=1;cas<=tt;cas++)
{
int n,m;
int cnt = 0;
memset(vis,0,sizeof(vis));
memset(num,0,sizeof(num));
scanf("%d%d",&n,&m);
for(int i=1;i<=sqrt(m);i++)//把因子全部筛出来
{
if(m%i==0)
{
ppp[cnt++]=i;
if(i*i!=m)
ppp[cnt++]=m/i;
}
}
sort(ppp,ppp+cnt);
for(int i=0;i<n;i++)
{
int x;scanf("%d",&x);
int kk = gcd(x,m);
for(int j=0;j<cnt;j++)
if(ppp[j]%kk==0)//说明这个因子的所有,都是可以被跳到的位置
vis[j]=1;
}
vis[cnt-1]=0;//显然 m是不可能被跳到的
long long ans = 0;
for(int i = 0; i < cnt; i++)
{
if(vis[i] != num[i])
{
int t = (m-1)/ppp[i];
ans += (long long)t*(t+1)/2 * ppp[i] * (vis[i]-num[i]);
//容斥一波
//一开始vis[i] - num[i] = 1的
//对于每个因数,如果重复计算了,在之后,减去就好了
t = vis[i] - num[i];
for(int j = i; j < cnt; j++)
if(ppp[j]%ppp[i] == 0)
num[j] += t;
}
}
printf("Case #%d: %lld\n",cas,ans);
}
}