从狄利克雷卷积到杜教筛【模板】

更多思路参考

洛谷日报是个好东西

一.狄利克雷卷积

①完全积性函数:\epsilon(n) = [n=1], I(n) = 1, id(n) = nϵ(n)=[n=1],I(n)=1,id(n)=n

引入他们因为前缀和好求,第一个为1,第二个为n,第三个为n(n+1)/2

不完全积性函数:\mu,\varphi

②公式:

(f*g)(n) = \sum \limits _{d | n} f(d) g(\frac{n}{d})

f一般为待求的,比如\mu,\varphi

g一般根据③中三个关系,自己构造的,比如I,id

③常用关系:

a.μ∗I=ϵ \varphi * I = idφ∗I=id \mu * id = \varphiμ∗id=φ\mu*I=e

b.\varphi*I=id 

c.\varphi=\mu*id(有上面两个式子得)

④例子:见下

二.杜教筛

  一般n达到1e9以上,线性筛TLE,就得上这个玩意了,可以O(n^{\frac{3}{4}})非线性时间解决。

①构造思路

核心式子:S为f的前缀和

g(1)S(n)=\sum\limits_{i=1}^{n}(f*g)(i) - \sum \limits _{i=2}^{n} g(i) S(\lfloor \frac{n}{i} \rfloor)(证明见链接

注意证明第二步到第三步\sum\limits_{d=1}^{n}\sum\limits_{d|i}\mu({i\over d})g(d)=\sum\limits_{d=1}^{n}g(d)\sum\limits_{i=1}^{\lfloor {n\over d}\rfloor}\mu(i)实际上是提出gd,然后用i替换i/d,  和式性质链接

a. f=\mu,由于\mu*I=e,我们设g=I,这样f*g=\epsilon       单位元\epsilon,取到1值为1,其他情况值为0,所以和就是1

b.f = \varphi,由于\varphi*I=id,我们设g = I , 这样f * g = id  id取到1~n,所以和为n(n+1)/2

模板&代码细节   BZOJ 3944 or P4213

先线性筛预处理一部分,然后剩下比较大的数没处理,用杜教筛(S_phi和S_mu部分),这时要递归+map找

#include <bits/stdc++.h>
#include<tr1/unordered_map>
using namespace std;
using namespace std::tr1;//头文件和std都要加,c++11可用

typedef long long ll;
typedef unsigned long long ull;
const int N = 5e6;
const int maxn=2147483647;//为了过此题,强设第二个上界 

ll mu[N+20],phi[N+20];
int prime[N+20];
int vis[N+20];
unordered_map<int,ll>ansmu,ansphi;//数组不够大,额外开,需要map
//unorder_map比普通map少了排序,会快一些 

inline int read() {   //输入挂
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
    return X*w;
}

void init()
{
	int cnt=0;
	phi[1]=mu[1]=1;
	for(int i=2;i<=N;i++)//统一ll和int!!!
	{
		if(!vis[i])    
		{
			prime[++cnt]=i;//++写在前面 
			phi[i]=i-1;//素数 p欧拉函数=p-1 
			mu[i]=-1;//素数  只有它本身一个素因子
		} 
		for(int j=1;prime[j]*i<=N&&j<=cnt;j++)//不越两界 
		{
			vis[i*prime[j]]=1;
			if(i%prime[j]==0)
			{
				//mu[i*prime[j]]=0;//初始化为0所以这项可有可无 
				phi[i*prime[j]] =phi[i]*prime[j];//性质
					break;
			}
			
				else
				{
					phi[i*prime[j]]=phi[i]*(prime[j]-1);//积性函数性质
					mu[i*prime[j]]=-mu[i];//多一个素因子变正负  
				}
				   
		}
	}
	for(int i=1;i<=N;i++)mu[i]+=mu[i-1],phi[i]+=phi[i-1];//前缀和 
}

ll S_phi(ll n)//分块和找超出预处理范围的数
{
	if(n<=N)return phi[n];//已经预处理过的 
	if(ansphi[n])return ansphi[n];//已经递归找到的
	ll ans=0;
	for(ll l=2,r;r<maxn&&l<=n;l=r+1)      //分块
	   r=n/(n/l) ,ans+=(r-l+1)*S_phi(n/l);  //I*phi,I和就是区间长度
	return ansphi[n]=(ull)n*(n+1ll)/2ll-ans;  //这题特殊,要转ull防溢出
}

ll S_mu(ll n)
{
	if(n<=N)return mu[n];//同上
	if(ansmu[n])return ansmu[n];
	ll ans=0;
	for(ll l=2,r;r<maxn&&l<=n;l=r+1)   //这里ll不然莫名超时
	   r=n/(n/l) ,ans+=(r-l+1)*S_mu(n/l);  //I*mu
	return ansmu[n]=1ll-ans;
}


int main()
{
init();
int T=read();
while(T--)
{
	int n=read();
	printf("%lld %lld\n",S_phi(n),S_mu(n));
 } 
 return 0;

}

猜你喜欢

转载自blog.csdn.net/zjyang12345/article/details/89319552