題意:
有\(m(1 \leq m \leq 10^9)\)個石子排成一圈,編號分別為\(0,1,2 \cdots m-1\)。
現在在\(0\)號石頭上有\(n(1 \leq n \leq 10^4)\)只青蛙。第\(i\)只青蛙每次能往前跳\(a_i\)步,但是他們跳的次數不加限制。
如果一塊石頭能至少被一只青蛙跳上去,那么稱這塊石頭被占領了。
求所有可能被占領的石頭的編號和。
分析:
首先我們應該發現這樣一個事實:
每次向前跳\(a_i\)步的效果和跳\(GCD(a_i, m)\)步是一樣的
所以可以對每個\(a_i\)和\(m\)求個\(GCD\),然后排序去重,這樣\(a_i\)的數量就會很少。
在第一個樣例中,\(m=12\),求完\(GCD\)的\(a_i\)為\((2, 3)\)。
那么被\(2\)占領的石塊的編號為\((0, 2, 4, 6, 8, 10)\);被\(3\)占領的石塊的編號為\((0, 3, 6, 9)\)。
顯然這樣是有重復的,\(6\)號石塊既會被\(2\)占領,又會被\(3\)占領。
和HDU 5528 Count a * b這題一樣,為了避免算重,我們規定:
編號為\(i\)的石塊只會被步長為\(GCD(i, m)\)的青蛙占領
在這之前我們還要預處理一下:
找出\(m\)所有的約數,然后用一個標記數組標記這些約數中存在哪些步長的青蛙。還要把所有步長的倍數也都標記上,也就是說有一個步長為\(d\)的青蛙,如果\(kd\)也是\(m\)的約數,我們可以假設還有一只步長為\(kd\)的青蛙存在。因為這樣并不影響最終的結果。
還是上面那個例子,對于步長為\(2\)的青蛙,我們只統計\((2,10)\)這兩個石塊。其他的\((4,8)\)這兩個石塊會在步長為\(4\)的青蛙中統計,\((6)\)這個石塊會在步長為\(6\)的青蛙中統計上。
一般地,對于步長為\(d\)的青蛙,我們要統計的石塊的個數就是\([1,\frac{m}ozvdkddzhkzd]\)互質的個數,即\(\phi(\frac{m}ozvdkddzhkzd)\)。
這些石塊的編號和為\(d \cdot \frac{\frac{m}ozvdkddzhkzd \phi(\frac{m}ozvdkddzhkzd)}{2}=m\frac{\phi(\frac{m}ozvdkddzhkzd)}{2}\)。
解釋一下上面公式:
其實我們只需要證明這樣一條公式:
\(n > 1\)時,\([1,n]\)中與\(n\)互質的數字之和為:\(\frac{n \phi(n)}{2}\)
證明:
因為\(GCD(n,i)=GCD(n,n-i)\),所以如果有\(GCD(n,i)=1\),那么也一定有\(GCD(n,n-i)=1\)成立。
所以滿足\(GCD(n,i)=1\)的數都是成對出現的,而且它倆的和為\(n\)。
還要證明一下,這樣不會算重,也就是不會出現\(i=n-i\)的情況。
因為如果\(i=n-i \Rightarrow n=2i\),得到\(n\)是偶數,\(i=\frac{n}{2}\)。顯然\(GCD(n,i) \neq 1\),推出矛盾。
上面是針對\(n>2\)的情況,幸運的是,\(n=2\)也滿足這條公式。
證畢。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#define MP make_pair
using namespace std;typedef long long LL;
typedef pair<int, int> PII;
const int maxp = 32000;int gcd(int a, int b) { return b == 0 ? a : gcd(b, a%b); }int pcnt;
int prime[maxp], phi[maxp];
bool vis[maxp];void preprocess() {for(int i = 2; i < maxp; i++) {if(!vis[i]) {prime[pcnt++] = i;phi[i] = i - 1;}for(int j = 0; j < pcnt && i * prime[j] < maxp; j++) {vis[i * prime[j]] = true;if(i % prime[j] == 0) {phi[i * prime[j]] = phi[i] * prime[j];break;}else phi[i * prime[j]] = phi[i] * (prime[j] - 1);}}
}LL Phi(int n) {if(n < maxp) return phi[n];LL ans = 1;for(int i = 0; i < pcnt; i++) {if(prime[i] * prime[i] > n) break;for(int j = 0; n % prime[i] == 0; j++) {if(!j) ans *= (prime[i] - 1);else ans *= prime[i];n /= prime[i];}}if(n > 1) ans *= (n - 1);return ans;
}const int maxn = 10000 + 10;
int n, m;
int a[maxn];vector<PII> divide;
vector<int> factors;
bool occupied[maxn];void dfs(int d, int x) {if(d == divide.size()) {factors.push_back(x);return ;}int p = divide[d].first, e = divide[d].second;for(int i = 0; i <= e; i++) {dfs(d+1, x);x *= p;}
}int main()
{preprocess();int T; scanf("%d", &T);for(int kase = 1; kase <= T; kase++) {printf("Case #%d: ", kase);scanf("%d%d", &n, &m);for(int i = 0; i < n; i++) {scanf("%d", a + i);a[i] = gcd(m, a[i]);}sort(a, a + n);n = unique(a, a + n) - a;if(a[0] == 1) { printf("%lld\n", (LL)m * (m-1) / 2); continue; }divide.clear();int t = m;for(int i = 0; i < pcnt; i++) {if(prime[i] * prime[i] > t) break;if(t % prime[i] == 0) {int cnt = 0;while(t % prime[i] == 0) {t /= prime[i];cnt++;}divide.push_back(MP(prime[i], cnt));}}if(t > 1) divide.push_back(MP(t, 1));factors.clear();dfs(0, 1);sort(factors.begin(), factors.end());int sz = factors.size();memset(occupied, false, sizeof(occupied));for(int i = 0; i < n; i++) {int k = lower_bound(factors.begin(), factors.end(), a[i]) - factors.begin();occupied[k] = true;}for(int i = 1; i < sz; i++) if(!occupied[i])for(int j = 0; j < i; j++) if(factors[i] % factors[j] == 0 && occupied[j]) {occupied[i] = true;break;}LL ans = 0;for(int i = 0; i < sz - 1; i++) if(occupied[i]) {int t = m / factors[i];ans += (LL)m * Phi(t) / 2;}printf("%lld\n", ans);}return 0;
}
轉載于:https://www.cnblogs.com/AOQNRMGYXLMV/p/4939910.html
總結
以上是生活随笔為你收集整理的HDU 5514 Frogs 欧拉函数的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。