題解 UVA11426 【拿行李(極限版) GCD - Extreme (II)】
原題地址UVA11426 拿行李(極限版) GCD - Extreme (II)
前置芝士:莫比烏斯反演,歐幾里得演算法
同類題目:UVA11417 GCD
只要你會用for迴圈,你就能過的題
純水經驗刷題數目的題,此題過後其他均可通過
正文:
\[\sum_{i=1}^{n}\sum_{j=i+1}^{n}\gcd(i,j) \]
你會發現在計算j時重複計算了i的那麼我們考慮先將其看作單獨的i,j,隨後將重複部分減去即可
\(\begin{aligned}\sum_{i = 1}^n\sum_{j = 1}^n\gcd(i, j)= \sum_{d = 1}^nd\sum_{i = 1}^n\sum_{j = 1}^n[\gcd(i, j) = d]\end{aligned}\)
到了這裡,我們發現對於\([gcd(i,j)=1]\)我們可以根據莫比烏斯函式的定義\(\begin{aligned}\sum_{d\mid n}\mu(d)\ =[n=1]\end{aligned}\)來進行替換,即\(\begin{aligned}\sum_{d=1}^n\sum_{i=1}^\frac{n}{d}\sum_{j=1}^\frac{n}{d}\sum_{p\mid i,j}\mu(p)\end{aligned}\)
我們可以按照套路將\(p\)提前\(\begin{aligned}\sum_{d=1}^n\sum_{p=1}^\frac{n}{d}\mu(p)\sum_{i=1}^\frac{n}{d}[p\mid i]\sum_{j=1}^\frac{n}{d}[p\mid j]\end{aligned}\)
對於後面的i,j兩個和我們進行整除分塊來做\(\begin{aligned}\sum_{d = 1}^nd\sum_{p=1}^{\left\lfloor\frac n d\right\rfloor}\mu(p)\left\lfloor\frac n {dp}\right\rfloor\left\lfloor\frac n {dp}\right\rfloor\end{aligned}\)
這裡運用一個dp化Q的技巧可以變為\(\begin{aligned}\sum_{Q = 1}^n\sum_{d|Q}d*\mu(\frac Q{d})\left\lfloor\frac n {Q}\right\rfloor^2\end{aligned}\)
然後你會發現我們化簡到的\(\begin{aligned}\sum_{d|Q}d*\mu(\frac Q{d})\end{aligned}\)符合\(id*\mu=\varphi\)的形式,所以我們可以進一步簡化為\(\begin{aligned}\sum_{Q = 1}^n\varphi(Q)\left\lfloor\frac n {Q}\right\rfloor^2\end{aligned}\),對於詢問T次值,總下來複雜度為\(O(n +T\sqrt n)\),可以通過此題,上面那個閹割版的柿子一模一樣,照著用程式碼實現即可,此題最後還要處理一下重複情況
常見卷積
\(σ(n)\):約數和函式,表示n的全部約數和
\(\mu*id_0=\epsilon\)
\(\varphi*id_0=id_1\)
\(\mu*id_1=\varphi\)
\(id_0*id_1=σ\)
\(Code\)
#include<bits/stdc++.h>
#define LL long long
using namespace std;
template <typename T> void read(T & t) {
t = 0;int f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')f =- 1;ch = getchar();}
do{t = t * 10 + ch - '0';ch = getchar();}while(ch >= '0' && ch <= '9');t *= f;
}
const int kato = 4e6 + 1;
bool ispri[kato];
LL n;
LL prime[kato] , phi[kato] , sum[kato] , cnt;
inline void get_phi(){
for(int i = 2;i <= kato;i ++){
if(!ispri[i]){
prime[++ cnt] = i;
phi[i] = i - 1;
}
for(int j = 1;j <= cnt && (i * prime[j] <= kato);j ++){
ispri[i * prime[j]] = 1;
if(i % prime[j] == 0){
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
else{
phi[i * prime[j]] = phi[i] * phi[prime[j]];
}
}
}
for(int i = 1;i <= kato;i ++){
// if(i<=10) cerr<<phi[i]<<"\n";
sum[i] = sum[i - 1] + phi[i];
}
}
inline int Ame_(){
phi[1] = 1 , ispri[1] = 1;
get_phi();
while(cin >> n){
if(!n){
return 0;
}
LL ans = 0;
for(LL l = 1 , r;l <= n;l = r + 1){
r = n / (n / l);
ans += (sum[r] - sum[l - 1]) * (n / l) * (n / l);
}
ans -= n * (n + 1) / 2;
ans /= 2;
printf("%lld\n" , ans);
}
return 0;
}
int Ame__ = Ame_();
signed main(){;}
注意
不知道為什麼n不開long long被卡精度卡了半天沒過,最後改了才沒事,其他的就沒什麼注意的點了