zoukankan      html  css  js  c++  java
  • 杜教筛学习笔记

    前置知识

    莫比乌斯反演

    莫比乌斯反演

    数论函数

    积性函数

    (gcd(a,b)=1),则(f(a imes b)=f(a) imes f(b))

    完全积性函数

    去掉 (gcd(a,b)=1) 的条件

    常见的积性函数

    恒等函数:(I(n)=1)

    单位函数:(id(n)=n)

    元函数:(epsilon(n)=[n=1])

    (以上三个函数也是完全积性函数)

    欧拉函数:(varphi(n)):小于 (n)(n) 互质的自然数个数

    莫比乌斯函数:(mu(n))

    约数和函数:(sigma_k(n)) 表示 (n) 的所有因数的 (k) 次方之和

    狄利克雷卷积

    定义

    两个函数 ((f,g)) 的狄利克雷卷积记为 (f*g)

    ((f * g)(n)=sum_{k mid n} f(k) imes gleft(frac{n}{k} ight))

    性质

    (1)、交换律 (f*g=g*f)

    (2)、结合律 (f*(g*h)=(f*g)*h)

    (3)、分配律 (f*h+g*h=(f+g)*h)

    (4)、若 (f,g) 为积性函数,则 (f*g) 也是积性函数

    常用卷积式

    (1)(mu*I=epsilon)

    (sum_{d|n}mu(d)=[n=1])

    也就是莫比乌斯函数性质一

    (2)(mu*id=varphi)

    (sum_{d|n}mu(d)frac{n}{d}=varphi(n))

    也就是莫比乌斯函数性质二

    (3)(varphi * I=id)

    (sum_{d|n}varphi(d)=n)

    杜教筛

    可以在低于线性的时间内筛出积性函数的前缀和

    如果我们要筛的积性函数为 (f)

    那么杜教筛的核心就是构造两个积性函数(h,g),使得 (h=f*g)

    并且 (h)(g) 的前缀和能够快速地求出来

    首先我们要用线性筛筛出一部分函数值,之后递归的时候要用到

    (s(n)=sumlimits_{i=1}^nf(i))

    (egin{aligned} sumlimits_{i=1}^n(f*g)(i) &=sumlimits_{i=1}^nsum_{d|i}f(d)g(frac{d}{i}) \&=sumlimits_{d=1}^ng(d)sum_{i=1}^{frac{n}{d}} f(i)\ &=sumlimits_{d=1}^ng(d)s(frac{n}{d}) \ &=g(1)s(n)+sumlimits_{d=2}^ng(d)s(frac{n}{d}) end{aligned})

    所以

    (s(n)g(1)=sumlimits_{i=1}^n(f*g)(i)-sumlimits_{d=2}^ng(d)s(frac{n}{d}))

    这样,我们就得到了一个关于 (s(n)) 的表达式

    前半部分是 (h) 函数的前缀和,可以快速得到

    后半部分可以进行整除分块递归求解,递归的时候要记忆化

    最后再整体除一个 (g(1)) 即可

    时间复杂度 (O(n^{frac{2}{3}}))

    杜教筛能够筛的积性函数不是很多

    主要是配合莫比乌斯反演使用

    (mu) 函数时利用 (mu*I=epsilon)

    (varphi) 函数时利用 (varphi * I=id)

    因此需要掌握一些常见的前缀和的公式

    代码

    P4213 【模板】杜教筛(Sum)

    #include<cstdio>
    #include<iostream>
    #include<map>
    #include<cstring>
    #define rg register
    inline int read(){
    	rg int x=0,fh=1;
    	rg char ch=getchar();
    	while(ch<'0' || ch>'9'){
    		if(ch=='-') fh=-1;
    		ch=getchar();
    	}
    	while(ch>='0' && ch<='9'){
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return x*fh;
    }
    typedef long long ll;
    const int maxn=5e6+5,mmax=5e6,mod=1e6+3;
    int pri[maxn];
    ll mu[maxn],phi[maxn];
    bool not_pri[maxn];
    struct has{
    	struct asd{
    		int nxt,num;
    		ll val;
    	}b[maxn];
    	int h[maxn],tot;
    	has(){
    		memset(h,-1,sizeof(h));
    		tot=1;
    	}
    	void insert(rg int num,rg ll val){
    		rg int now=num%mod;
    		b[tot].nxt=h[now];
    		b[tot].val=val;
    		b[tot].num=num;
    		h[now]=tot++;
    	}
    	ll cx(rg int num){
    		rg int now=num%mod;
    		for(rg int i=h[now];i!=-1;i=b[i].nxt){
    			if(b[i].num==num) return b[i].val;
    		}
    		return -1;
    	}
    }ans_phi,ans_mu;
    void xxs(){
    	not_pri[0]=not_pri[1]=1;
    	mu[1]=phi[1]=1;
    	for(rg int i=2;i<=mmax;i++){
    		if(!not_pri[i]){
    			pri[++pri[0]]=i;
    			mu[i]=-1;
    			phi[i]=i-1;
    		}
    		for(rg int j=1;j<=pri[0] && 1LL*i*pri[j]<=mmax;j++){
    			not_pri[i*pri[j]]=1;
    			if(i%pri[j]==0){
    				phi[i*pri[j]]=phi[i]*pri[j];
    				mu[i*pri[j]]=0;
    				break;
    			} else {
    				phi[i*pri[j]]=phi[i]*phi[pri[j]];
    				mu[i*pri[j]]=-mu[i];
    			}
    		}
    	}
    	for(rg int i=1;i<=mmax;i++){
    		mu[i]+=mu[i-1];
    		phi[i]+=phi[i-1];
    	}
    }
    ll getsum_mu(rg int now){
    	if(now<=mmax) return mu[now];
    	if(ans_mu.cx(now)!=-1) return ans_mu.cx(now);
    	ll ans=1;
    	for(rg int l=2,r=0;r<now;l=r+1){
    		r=now/(now/l);
    		ans-=1LL*(r-l+1)*getsum_mu(now/l);
    	}
    	ans_mu.insert(now,ans);
    	return ans;
    }
    ll getsum_phi(rg int now){
    	if(now<=mmax) return phi[now];	
    	if(ans_phi.cx(now)!=-1) return ans_phi.cx(now);
    	ll ans=1LL*now*(now+1LL)/2;
    	for(rg int l=2,r=0;r<now;l=r+1){
    		r=now/(now/l);
    		ans-=1LL*(r-l+1)*getsum_phi(now/l);
    	}
    	ans_phi.insert(now,ans);
    	return ans;
    }
    int t,n;
    int main(){
    	xxs();
    	t=read();
    	while(t--){
    		n=read();
    		printf("%lld %lld
    ",getsum_phi(n),getsum_mu(n));
    	}
    	return 0;
    }
    
  • 相关阅读:
    Codeforces Round #649 (Div. 2) D. Ehab's Last Corollary
    Educational Codeforces Round 89 (Rated for Div. 2) E. Two Arrays
    Educational Codeforces Round 89 (Rated for Div. 2) D. Two Divisors
    Codeforces Round #647 (Div. 2) E. Johnny and Grandmaster
    Codeforces Round #647 (Div. 2) F. Johnny and Megan's Necklace
    Codeforces Round #648 (Div. 2) G. Secure Password
    Codeforces Round #646 (Div. 2) F. Rotating Substrings
    C++STL常见用法
    各类学习慕课(不定期更新
    高阶等差数列
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/14260731.html
Copyright © 2011-2022 走看看