1. 程式人生 > 實用技巧 >題解 UVA11426 【拿行李(極限版) GCD - Extreme (II)】

題解 UVA11426 【拿行李(極限版) GCD - Extreme (II)】

原題地址UVA11426 拿行李(極限版) GCD - Extreme (II)

前置芝士:莫比烏斯反演,歐幾里得演算法

同類題目:UVA11417 GCD

只要你會用for迴圈,你就能過的題

P1390 公約數的和

SP3871 GCDEX - GCD Extreme

UVA11424 GCD - Extreme (I)

P2398 GCD SUM

純水經驗刷題數目的題,此題過後其他均可通過

正文:

\[\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被卡精度卡了半天沒過,最後改了才沒事,其他的就沒什麼注意的點了