题目链接:
题目描述:
有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)。
1 #include <cmath> 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #include <algorithm> 6 using namespace std; 7 typedef __int64 LL; 8 const int maxn = 100010; 9 10 int p[maxn], vis[maxn], num[maxn]; 11 int gcd (int a, int b) 12 { 13 return b==0 ? a : gcd(b, a%b); 14 } 15 16 int main () 17 { 18 int T; 19 scanf ("%d", &T); 20 for (int t=1; t<=T; t++) 21 { 22 int n, m, cnt = 0; 23 scanf ("%d %d", &n, &m); 24 for (int i=1; i<=sqrt(m); i++) 25 { 26 if (m % i == 0) 27 { 28 p[cnt ++] = i; 29 if (i * i != m) 30 p[cnt ++] = m / i; 31 } 32 } 33 sort (p, p+cnt); 34 int u; 35 memset (vis, 0, sizeof(vis)); 36 memset (num, 0, sizeof(num)); 37 for (int i=0; i<n; i++) 38 { 39 scanf ("%d", &u); 40 int temp = gcd (u, m); 41 for (int j=0; j<cnt; j++) 42 { 43 if (p[j] % temp == 0) 44 vis[j] = 1; 45 } 46 } 47 vis[cnt-1] = 0; 48 LL ans = 0; 49 for (int i=0; i<cnt; i++) 50 { 51 if (vis[i] != num[i]) 52 { 53 LL temp = m / p[i]; 54 ans += temp * (temp - 1) / 2 * p[i] * (vis[i] - num[i]); 55 temp = vis[i] - num[i]; 56 for (int j=i; j<cnt; j++) 57 if (p[j] % p[i] == 0) 58 num[j] += temp; 59 } 60 } 61 printf ("Case #%d: %I64d ", t, ans); 62 } 63 return 0; 64 }