狄利克雷卷積 與 杜教篩


先放上板題
BZOJ3944
洛谷P4213

嗯,杜教篩解決的就是這樣一個喪心病狂的前綴和
\(O(N)\)都會T。。

積性函數##

如果一個數論函數\(f(n)\),滿足若\(m,n\)互質,那么有\(f(n * m) = f(n) * f(m)\),那么稱\(f(n)\)為積性函數

特別的,如果對於任意\(n,m\)都滿足\(f(n * m) = f(n) * f(m)\),那么稱\(f(n)\)為完全積性函數

狄利克雷卷積##

對於兩個積性函數\(f(n),g(n)\),定義它們的狄利克雷卷積為:\((f*g)(n) = \sum_{d|n} f(d) * g(\frac{n}{d})\)
數論函數與狄利克雷卷積形成群,滿足結合律,封閉性,單位元,逆元,同時還滿足交換律

其中單位元為\(\epsilon\)\(\epsilon(n) = [n = 1]\)
還有一些比較常用的積性數論函數
積性函數

這里有比較常用的幾種卷積關系:
\(\mu * 1=\epsilon\)【莫比烏斯反演】【\(\mu\)\(1\)互為逆元】
\(\phi * 1=Id\) \(\qquad \phi = Id * \mu\)
\(d = 1 * 1\) \(\qquad 1 = \mu * d\)
等等之類的,,

我們甚至可以簡單地證明莫比烏斯反演了:

\[F = f * 1 \]

\[f = \mu * F \]

杜教篩##

說了那么多,回歸到杜教篩吧:
我們要求的是:

\[S(n) = \sum_{i=1}^{n} f(i) \]

我們找到另一個積性數論函數\(g(n)\)
那么

\[\sum_{i=1}^{n}(f*g)(i) = \sum_{i=1}^{n} \sum_{d|i} g(d) * f(\frac{i}{d}) \]

\[\qquad \qquad = \sum_{i=1}^{n} g(i) * \sum_{j = 1}^{\lfloor \frac{n}{i} \rfloor} f(j) \]

\[\qquad \qquad = \sum_{i=1}^{n} g(i) * S(\lfloor \frac{n}{i} \rfloor) \]

我們就有:

\[\sum_{i=1}^{n}(f*g)(i) = \sum_{i=1}^{n} g(i) * S(\lfloor \frac{n}{i} \rfloor) \]

就可以有:

\[g(1) * S(n) = \sum_{i=1}^{n}(f*g)(i) - \sum_{i=2}^{n} g(i) * S(\lfloor \frac{n}{i} \rfloor) \]

如果我們能找到一個函數\(g(n)\)
使得\(\sum_{i=1}^{n}(f*g)(i)\)可以被快速計算
並且其前綴和非常容易計算【因為要計算\(\sum_{i=2}^{n} g(i) * S(\lfloor \frac{n}{i} \rfloor)\)
那么我們就可以 分塊 + 遞歸 求解\(S(n)\)

可以證明,我們預處理出積性函數\(f(n)\)的前\(O(n^{\frac{2}{3}})\)項,就可以在記憶化搜索在\(O(n^{\frac{2}{3}})\)的復雜度計算出\(S(n)\)
是不是很神奇?

回到例題
具體地,兩個問都可以考慮令\(g = 1\)
具體自行思考

呼啦啦寫完啦
貼代碼【洛谷AC,BZOJ RE不停,求助QAQ,,或者哪天我再查查】
【思路還是沒問題】

#include<iostream>
#include<cstdio>
#include<cmath>
#include<map>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
using namespace std;
const int maxn = 100005,maxm = 4000005,N = 3414680,INF = 1000000000;
map<LL,LL> _mu,_phi;
int isn[maxm];
LL p[maxm],pi;
LL mu[maxm],phi[maxm];
void init(){
	mu[1] = 1; phi[1] = 1;
	for (register LL i = 2; i < N; i++){
		if (!isn[i]) p[++pi] = i,mu[i] = -1,phi[i] = i - 1;
		for (register int j = 1; j <= pi && i * p[j] < N; j++){
			isn[i * p[j]] = true;
			if (i % p[j] == 0){
				mu[i * p[j]] = 0;
				phi[i * p[j]] = phi[i] * p[j];
				break;
			}
			mu[i * p[j]] = -mu[i];
			phi[i * p[j]] = phi[i] * (p[j] - 1);
		}
	}
	for (register int i = 1; i < N; i++){
		mu[i] += mu[i - 1];
		phi[i] += phi[i - 1];
	}
}
LL S1(LL n){
	if (n < N) return phi[n];
	map<LL,LL>::iterator it;
	if ((it = _phi.find(n)) != _phi.end())
		return it->second;
	LL ans = n * (n + 1) >> 1;
	for (int i = 2,nxt; i <= n; i = nxt + 1){
		nxt = n / (n / i);
		ans -= (nxt - i + 1) * S1(n / i);
	}
	return _phi[n] = ans;
}
LL S2(LL n){
	if (n < N) return mu[n];
	map<LL,LL>::iterator it;
	if ((it = _mu.find(n)) != _mu.end())
		return it->second;
	LL ans = 1;
	for (int i = 2,nxt; i <= n; i = nxt + 1){
		nxt = n / (n / i);
		ans -= (nxt - i + 1) * S2(n / i);
	}
	return _mu[n] = ans;
}
int main(){
	init();
	LL T,n;
	scanf("%lld",&T);
	while (T--){
		scanf("%lld",&n);
		printf("%lld %lld\n",S1(n),S2(n));
	}
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM