题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5514
题目描述:
有n只青蛙,m个石头(围成圆圈)。第i只青蛙每次只能条ai个石头,问最后所有青蛙跳过的石头的下标总和是多少?
解题思路:
第i只青蛙只能走到gcd(ai, m)的位置,我们就可以把m的因子提取出来,然后对青蛙能走到的因子位置打标记。青蛙能走到vis[i]因子位置就把vis[i]标记为1,然后num[i]表示m的第i个因子泪加的次数。如果vis[i]!=num[i]的话,就更新num。因子的个数大概是log2(m),所以时间复杂度大概为O(log2(m)2)。
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 10;
int vis[maxn], num[maxn];
int p[maxn];
int gcd(int a, int b)
{
return b? gcd(b, a%b) : a;
}
int main()
{
int T, n, m, pos, a;
scanf("%d", &T);
for(int cas = 1; cas <= T; cas++)
{
scanf("%d%d", &n, &m);
int k = (int)sqrt(m);
pos = 0;
for(int i = 1; i <= k; i++)
{
if(m%i == 0)
{
p[pos++] = i;
if(i*i != m)
p[pos++] = m/i;
}
}
memset(vis, 0, sizeof(vis));
memset(num, 0, sizeof(num));
sort(p, p+pos);
for(int i = 0; i < n; i++)
{
scanf("%d", &a);
int t = gcd(a, m);
for(int j = 0; j < pos; j++)
if(p[j]%t == 0)
vis[j] = 1;
}
vis[pos-1] = 0;
LL ans = 0;
for(int i = 0; i < pos; i++)
{
if(vis[i] != num[i])
{
int t = (m-1)/p[i];
ans += (LL)t*(t+1)/2 * p[i] * (vis[i]-num[i]);
t = vis[i] - num[i];
for(int j = i; j < pos; j++)
if(p[j]%p[i] == 0)
num[j] += t;
}
}
printf("Case #%d: %lld\n", cas, ans);
}
return 0;
}