題目鏈接:
題目描述:
有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\n", t, ans); 62 } 63 return 0; 64 }
