【杜教篩模板】洛谷P4213

P4213
Attention
本題極度卡常,不需要long long的情況下儘量使用int,mu函數不需要使用long long。

杜教篩用於求積性函數前綴和。

例如:求 i=1nmu[i]\sum_{i=1}^{n} mu[i] 或者 i=1nphi[i]\sum_{i=1}^{n} phi[i]

杜教篩公式:求S(n)=i=1nf(i)S(n) = \sum_{i=1}^{n} f(i)

g(i)g(i) 爲一個積性函數,常見可用積性函數有:
ϕ(n)\phi(n) (歐拉函數,不大於n且與n互質的數的個數)
μ(n)\mu(n) (莫比烏斯函數)
d(n)d(n) (約數個數,d(n)=in1d(n) = \sum_{i|n} 1)
σ(n)\sigma(n) (約數和函數,σ(n)=ini\sigma(n) = \sum_{i|n} i)

一些完全積性函數,在使用的時候更方便簡單:
e(n)=[n==1]e(n) = [n==1]
I(n)=1I(n) = 1
id(n)=nid(n) = n
然後杜教篩就要來啦!

對於兩個積性函數ffgg,有
i=1n(fg)(i)=i=1nxy=if(x)g(y)=y=1ng(y)xy<=nf(x)=y=1ng(y)S(ny)\sum_{i=1}^{n} (f*g)(i) = \sum_{i=1}^{n} \sum_{xy=i} f(x)g(y) = \sum_{y=1}^{n} g(y) \sum_{xy<=n} f(x) = \sum_{y=1}^{n}g(y)S(\lfloor \frac{n}{y} \rfloor)

將上式變形之後可以發現:
g(1)S(n)=i=1n(fg)(i)y=2ng(y)S(ny)g(1)S(n) = \sum_{i=1}^{n} (f*g)(i) - \sum_{y=2}^{n}g(y)S(\lfloor \frac{n}{y}\rfloor)

至於是怎麼變形的大家應該看得懂的。
如果我們可以用一個g把i=1n(fg)(i)\sum_{i=1}^{n}(f*g)(i)給搞出來,那麼我們就可以算出來S(n)了。
使用數論分塊還可以把上式後面的複雜度給降到O(n\sqrt n)

現在我們已經對杜教篩有一個基本的瞭解了。
然後看一下模板題怎麼做。
題目要求i=1nϕ(i)\sum_{i=1}^{n} \phi(i)i=1nμ(i)\sum_{i=1}^{n} \mu(i)

g(i)=Ig(i) = I,那麼ϕ(i)I=id(i)\phi(i) * I = id(i)μ(i)I=ϵ\mu(i) * I = \epsilon
其中ϵ\epsilon的前綴和爲1,id(i)id(i)的前綴和爲 n*(n+1)/2
i=abg(i)=(b(a1))\sum_{i=a}^{b} g(i) = (b-(a-1))

首先用線性篩預處理出大概5e6的mu和phi的前綴和,不夠的用杜教篩慢慢算就好了。

詳細的看代碼吧:

/*
杜教篩模板 
求phi(i)和mu(i)的前綴和
有 E = [n=1], I(n) = 1, id(n) = n
由於mu * I = E 
令f = mu, g = I, (f*g) = E
故 g(1)*Smu(n) = sigma(E) - sigma(g(i)*S(n/i)) (2<=i<=n)
令f = phi, g = I, (f*g) = id 
故 g(1)*Sphi(n) = sigma(id(i)) [(1<=i<=n)] - sigma(g(i)*S(n/i)) (2<=i<=n)
複雜度 O(2e9能過) 
*/ 
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e6+7;
int mu[maxn],pri[maxn];
bool npri[maxn];
ll phi[maxn];
unordered_map<int,int> summu;
unordered_map<int,ll> sumphi;
inline void init()
{
	npri[0] = npri[1] = 1;
	mu[1] = phi[1] = 1;
	for(int i=2;i<maxn;i++)
	{
		if(!npri[i]) pri[++pri[0]] = i,mu[i] = -1,phi[i] = i-1;
		for(int j=1;j<=pri[0] && i*pri[j]<maxn;j++)
		{
			npri[i*pri[j]] = 1;
			if(i%pri[j])
			{
				mu[i*pri[j]] = -mu[i];
				phi[i*pri[j]] = phi[i]*phi[pri[j]];
			}
			else
			{
				mu[i*pri[j]] = 0;
				phi[i*pri[j]] = phi[i]*pri[j];
				break;
			}
		} 
	}
	for(int i=1;i<maxn;i++) mu[i] += mu[i-1],phi[i] += phi[i-1];
}
inline int gsum(int x) // g(i)的前綴和 
{
	return x;
}
inline int getsmu(int x)
{
	if(x<maxn) return mu[x];
	if(summu[x]) return summu[x];
	int ans = 1;
	for(int l=2,r;l<=x;l=r+1)
	{
		r = x/(x/l);
		ans -= (gsum(r)-gsum(l-1))*getsmu(x/l);
	}
	return summu[x] = ans/gsum(1);
}
inline ll getsphi(int x)
{
	if(x<maxn) return phi[x];
	if(sumphi[x]) return sumphi[x];
	ll ans = 1LL*x*(x+1)/2;
	for(int l=2,r;l<=x;l=r+1)
	{
		r = x/(x/l);
		ans -= (gsum(r)-gsum(l-1))*getsphi(x/l);
	}
	return sumphi[x] = ans/gsum(1);
}
int main()
{
	init();
	int T;
	scanf("%d",&T);
	while(T--)
	{
		int n;
		scanf("%d",&n);
		printf("%lld %d\n",getsphi(n),getsmu(n));
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章