SDOI2012 Longge的問題
阿新 • • 發佈:2018-12-13
題目描述很簡潔,求\(\sum_{i=1}^ngcd(i,n)\)
由於我們難以直接求出\(gcd\),所以我們換一種比較套路的做法:列舉\(gcd\),轉化為數論函式計算。
根據尤拉函式的性質:\(n = \sum_{d|n}\varphi(d)\),那麼我們就能把式子改寫一下,得到
\[\sum_{i=1}^n\sum_{d|i,d|n} \varphi(d)\]
轉換一下可以得到:
\[\sum_{i=1,d|i}^n\sum_{d|n} \varphi(d)\]
前面那一項就是\(\frac{n}{d}\) ,把兩項調換一下
所以結果就是 \[\sum_{d|n}d \varphi(\frac{n}{d})\]
或者我們可以換一種推導方法,同樣是列舉gcd,這次我們改寫成這個形式:
\[\sum_{d|n}\sum_{i=1}^n[gcd(i,n) == d]\]
把d除進去,就有:
\[\sum_{d|n}\sum_{i=1}^{\frac{n}{d}}[gcd(i,\frac{n}{d}) == 1]\]
然後後面其實就是一個尤拉函式的形式,所以結果就是:
\[\sum_{d|n} d \varphi(\frac{n}{d})\]
我們只要每次在\(\sqrt{n}\)的範圍內列舉質因子,然後在\(O(\sqrt{n})\)的時間內計算尤拉函式即可。注意要開\(longlong\)
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<set> #include<vector> #include<map> #include<queue> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') #define fr friend inline #define y1 poj #define mp make_pair #define pr pair<int,int> #define fi first #define sc second #define pb push_back #define I puts("bug") using namespace std; typedef long long ll; const int M = 200005; const int INF = 1000000009; const double eps = 1e-7; const double pi = acos(-1); const ll mod = 1e9+7; ll read() { ll ans = 0,op = 1;char ch = getchar(); while(ch < '0' || ch > '9') {if(ch == '-') op = -1;ch = getchar();} while(ch >= '0' && ch <= '9') ans = ans * 10 + ch - '0',ch = getchar(); return ans * op; } ll n,p[M],tot; bool np[M]; ll phi(ll x) { ll ans = x,m = sqrt(x); rep(i,2,m) if(!(x % i)) {ans = ans - ans / i; while(!(x%i)) x /= i;} if(x > 1) ans = ans - ans / x; return ans; } ll calc(ll x) { ll cur = 0; for(int i = 1;(ll)i * i < x;i++) if(x % i == 0) cur += (ll)i * phi(x/i) + (ll)(x/i) * phi(i); if((ll)sqrt(x) * sqrt(x) == x) cur += (ll)sqrt(x) * phi(sqrt(x)); return cur; } int main() { n = read(); printf("%lld\n",calc(n)); return 0; }