1. 程式人生 > 實用技巧 >《關於上一次講課莫比烏斯反演出鍋了所以整理一下那些事》

《關於上一次講課莫比烏斯反演出鍋了所以整理一下那些事》

上次講課用莫比烏斯反演的時候講出鍋了,所以總結一下

前置芝士

整除分塊

----轉載自dalao部落格

問題:求\(\sum_{i=1}^N \lfloor \frac Ni \rfloor\),其中\(N \leq 10^{12}\)
對於\(\large \lfloor \frac Ni \rfloor\)最多可存在\(2\sqrt{N}\)種取值
可以分類討論\(i\)的取值,當\(i\le \sqrt{N}\)時,存在\(\sqrt{N}\)種取值,而對於\(i\gt\sqrt{N},\large{\frac Ni}\lt\sqrt{N}\),最多也僅有\(\sqrt{N}\)種取值,所以最多可存在\(2\sqrt{N}\)

種取值
另外的話設\(\large \lfloor \frac N{i'} \rfloor\)\(\large \lfloor \frac Ni \rfloor\)相等,那麼對於\(i'\)最大值為\(\large \left \lfloor \frac N{\left \lfloor \frac Ni \right \rfloor\ } \right \rfloor\)

證明的話我們對於\(\large{ \lfloor \frac Ni \rfloor}=k\),可以轉化為\(ki+r=N\),其中的\(1\le\ r\lt\ i\),那麼對於\(\large{\lfloor \frac N{i+d} \rfloor}=k\)

,則有\(k(i+d)+r'=N\),則有\(r'=r-kd\),當\(r'=0\)時,\(d\)可取的最大值為\(\large \lfloor \frac rk \rfloor\)

於是可以算出\(\begin{aligned}i'=i+d_{max} =i+\lfloor \frac rk \rfloor =i+\left \lfloor \frac {N \;mod\; i}{\lfloor \frac Ni \rfloor} \right \rfloor =i+\left \lfloor \frac {N-\lfloor \frac Ni\rfloor i}{\lfloor \frac Ni \rfloor} \right \rfloor =\left \lfloor i + \frac {N-\lfloor \frac Ni\rfloor i}{\lfloor \frac Ni \rfloor} \right \rfloor =\left \lfloor \frac{\lfloor \frac Ni \rfloor i}{\lfloor \frac Ni \rfloor} + \frac {N-\lfloor \frac Ni\rfloor i}{\lfloor \frac Ni \rfloor} \right \rfloor =\left \lfloor \frac N{\lfloor \frac Ni \rfloor} \right \rfloor \quad \quad\end{aligned}\)

然後可以列舉兩個變數\(l,r\),其中\(l\)初始值為1,每次可以令\(\large r=\left \lfloor \frac N{\lfloor \frac NL \rfloor} \right \rfloor\),隨後將\(\large (r-l+1)\cdot \lfloor \frac NL \rfloor\)對答案進行累加,隨後令\(l=r+1\),依次增加累加,最後對於分塊複雜度可以看為\(O(\sqrt N)\)

積性函式

之前我所說的可能有錯現在從新進行一次宣告吧算是,對於任意 \(x,y \in \mathbb{N}_{+},\gcd(x,y)=1\),都有\(f(xy)=f(x)f(y)\),則\(f(n)\)為積性函式

\(f(n)\)\(g(n)\)為積性函式,則以下的情況也為積性函式

\(\begin{aligned}h(x)=&f(x^p)\\h(x)=&f^p(x)\\h(x)=&f(x)g(x)\\h(x)=&\sum_{d\mid x}f(d)g(\frac{x}{d})\end{aligned}\)

積性函式的例子:\(e(n)=[n=1]\)

\(1(n)=1\)

\(\mu(n)=\begin{cases}(-1)^k&n=p_1p_2p_3\dots p_k\\0&n=p^2q\\1&n=1\end{cases}\)

\(\varphi(n)=\sum_{i=1}^n[\gcd(i,n)=1]\)

\(\operatorname{id}_k(n)=n^k\operatorname{id}_{1}(n)\)

狄利克雷Dirichlet卷積

定義(不是推出來的是定義):定義兩個數論函式\(f,g\)卷積為\((f*g)(n)=\sum_{d|n}f(d)g(\frac nd)\)

一些性質

卷積滿足交換律和結合律

數論函式\(e\)為卷積中的單位元即任何函式卷\(e\)都為其本身

栗子:\(\mu*id_0=\epsilon\)

\(\varphi*id_0=id_1\)

\(\mu*id_1=\varphi\)

\(id_0*id_1=σ\)

莫比烏斯函式

\(\mu(n)=\begin{cases}(-1)^k&k為n的本質不同質因子個數\\0&n含有平方因子\\1&n=1\end{cases}\)

性質

是積性函式

\(\sum_{d\mid n}\mu(d)=\begin{cases}1&n=1\\0 & n≠1\\ \end{cases}\)

即為\(\mu*id_0=\epsilon\)

證明的話不會,就這樣

線性篩\(\mu\)應該都知道吧

inline void pri(){
	for(int i = 2;i <= N;i ++){
        if(!ispri[i]){
			prime[++ cnt] = i;
			mu[i] =- 1;
    	}
        for(int j = 1;j <= cnt && (i * prime[j] <= N);j ++){
            ispri[i*prime[j]] = true;
            if(i % prime[j]==0){
                  break;
            }
            else{
            	mu[i * prime[j]] =- mu[i];
            }
        }
}

但是我會證明\(\mu*id_1=\varphi\)下面是證明

\(\begin{aligned}\varphi*id_0&=\sum_{d\mid n}\varphi(\frac{n}{d})\\&=\sum_{i=0}^c\varphi(p^1)\\&=p^c\\&=id_1\end{aligned}\)

在等式兩邊同時捲上一個\(\mu\)就可以得到\(\varphi(n)=\sum_{d\mid n}d*\mu(\frac{n}{d})\)
即為\(\mu*id_1=\varphi\)

正文:莫比烏斯反演

設有\(f(n)\),\(g(n)\)兩個數論函式

若存在\(f(n)=\sum_{d|n}g(d)\iff g(n)=\sum_{d|n}\mu(\frac nd)f(d)\)

其實並沒有什麼用處,相當於是廢話,證明的話你可以看作\(f=g*1\),然後證明\(g=f*\mu\)

\(f=g*1\)兩邊同時捲上一個\(\mu\)之後你可以得到\(f*\mu=g*1*\mu\)即為\(f*\mu=g*e\),則可得出\(g=f*\mu\)

題目的話

UVA11426 拿行李(極限版) GCD - Extreme (II)

\[\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 gcd(i,j)}\mu(p)\end{aligned}\)

我們可以按照套路將\(p\)提前,變換求和順序,先列舉\(d\mid gcd(i,j)\) 可得\(\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}\)

易知\(1\sim\lfloor\dfrac{n}{d}\rfloor\)中p的倍數有個\(\lfloor\dfrac{n}{dp}\rfloor\),故原式化為對於後面的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)\),可以通過此題,上面那個閹割版的柿子一模一樣,照著用程式碼實現即可,此題最後還要處理一下重複情況

#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(){;}

P2257 YY的GCD

題目所給為\(\begin{aligned}\sum_{i=1}^n\sum_{j=1}^m[gcd(x,y)\in prime]\end{aligned}\)

\(\begin{aligned}=\sum_{p\in prime}\sum_{i=1}^n\sum_{j=1}^m[gcd(x,y)=p]\end{aligned}\)

\(\begin{aligned}=\sum_{p\in prime}\sum_{i=1}^n\sum_{j=1}^m[gcd(\frac{x}{p},\frac{y}{p})=1]\end{aligned}\)

\(\begin{aligned}=\sum_{p\in prime}\sum_{i=1}^\frac{n}{p}\sum_{j=1}^\frac{m}{p}[gcd(x,y)=1]\end{aligned}\)

到了這裡,我們發現對於\([gcd(i,j)=1]\)我們可以根據莫比烏斯函式的定義\(\begin{aligned}\sum_{d\mid n}\mu(d)\ =[n=1]\end{aligned}\)來進行替換,即

\(\begin{aligned}\sum_{p\in prime}\sum_{i=1}^\frac{n}{p}\sum_{j=1}^\frac{m}{p}\sum_{d\mid i,j}\mu(d)\end{aligned}\)

然後重點來了,你粗略地看著個柿子可以看為\(\begin{aligned}\sum_{p}\sum_{i}\sum_{j}\sum_{d=1}\mu(d)[d\mid i][d\mid j]\end{aligned}\),其中對\(\mu(d)\)有影響的為\(p\)可以提到\(i\)前面,\([d\mid i]\),\([d\mid j]\)同理可得,則有下一步柿子

\(\begin{aligned}\sum_{p\in prime}\sum_{d=1}^\frac{n}{p}\mu(d)\sum_{i=1}^\frac{n}{p}[d\mid i]\sum_{j=1}^\frac{m}{p}[d\mid j]\end{aligned}\)

然後用之前所學過的整除分塊對\([d\mid i]\), \([d\mid j]\)處理一下

\(\begin{aligned}\sum_{p\in prime}\sum_{d=1}^\frac{n}{p}\mu(d)\left \lfloor \frac{n}{dp} \right \rfloor\left \lfloor \frac{m}{dp} \right \rfloor\end{aligned}\)

將dp用k更換之後有\(\begin{aligned}\sum_{k=1}^n\sum_{p\in prime}\mu( \frac{k}{p} )\left \lfloor \frac{n}{k} \right \rfloor\left \lfloor \frac{m}{k} \right \rfloor\end{aligned}\)

最後在篩的時候維護一個字首和即可

#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 = 1e7 + 10;

int prime[kato >> 1] , cnt , t;

int mu[kato] , num[kato];

bool ispri[kato];

inline int my_swap(int &x , int &y){
	return x ^= y ^= x ^= y ;
} 

inline void pri(){
	for(int i = 2;i <= kato - 10;i ++){
		if(!ispri[i]){
			prime[++ cnt] = i;
			mu[i] =- 1;
		}
		for(int j = 1;j <= cnt && (i * prime[j] <= kato - 10);j ++){
			if(i * prime[j] <= kato - 10){
				ispri[i * prime[j]] = 1;
			}
			if(i % prime[j] == 0){
				break;
			}
			else{
				mu[i * prime[j]] =- mu[i];
			}
		}
	}
	for(int i = 1;i <= cnt;i ++){
		int res = 1;
		for(int j = prime[i];j <= kato - 1;j += prime[i] , res ++){
			num[j] += mu[res];
		}
	}
	for(int i = 2;i <= kato - 10;i ++){
		num[i] += num[i - 1];
	}
}

inline int Ame_(){
	mu[1] = 1;
	pri();
	read(t);
	for(int l , r , n , m; t --> 0 ;){
		read(n);read(m);
		LL ans = 0;
		if(n > m){
			my_swap(n , m); 
		}
		for(l = 1 , r;l <= n;l = r + 1){
			r = min(n / (n / l) , m / (m / l));
			ans += 1LL * (num[r] - num[l - 1]) * (n / l) * (m / l);
		}
		printf("%lld\n" , ans);
	}
	return 0;
}

int Ame__ = Ame_();

int main(){;}