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;
    }
    
  • 相关阅读:
    英特尔®oneAPI简介及动手实验研讨会召集令
    发展壮大:帮助独立游戏开发商解决分销难题
    我们可以从英特尔® SPMD 程序编译器中学到什么?
    2019 Unreal Open Day —— 英特尔携手 UE 助力游戏开发生态建设
    Abp集成Quartz.net记录
    静态和实例初始化映射
    Queryable扩展点
    投影
    空类型映射
    列表和数组
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/14260731.html
Copyright © 2011-2022 走看看