【Bzoj3944】杜教筛模板(狄利克雷卷积搞杜教筛)

  题目链接

  哇杜教筛超炫的

  有没有见过$O(n^\frac{2}{3})$求欧拉函数前缀和的算法?没有吧?蛤蛤蛤

  首先我们来看狄利克雷卷积是什么

  首先我们把定义域是整数,陪域是复数的函数叫做数论函数。

  然后狄利克雷卷积是个函数和函数的运算。

  比如说有两个数论函数f,g

  那么它们的狄利克雷卷积就是f*g,记为h

  然后我们惊奇地发现$h(i)=\sum\limits_{d|i}f(d)g(\frac{i}{d})$

  而且狄利克雷卷积好像是个群,然后它就能满足交换律结合律分配律balaba

  那么这个玩意有什么卵用呢?

  (显然它很有卵用)

  我们再说一个数论函数叫单位元。

  e(1)=1 e(n)=0(n>1)

  然后我们发现任意函数f(x)有f*e=f

  然后就引出我们的杜教筛,这里计算莫比乌斯函数的前缀和

  设$S(n)=\sum\limits_{i=1}^{n}miu(i)$

  然后我们随便代一个数论函数g,套上狄利克雷卷积

  $\sum\limits_{i=1}^{n}(g*miu)(i)=\sum\limits_{i=1}^{n}\sum\limits_{d|i}miu(\frac{i}{d})g(d)$

  $=\sum\limits_{d=1}^{n}\sum\limits_{d|i}miu(\frac{i}{d})g(d)$

  $=\sum\limits_{d=1}^{n}g(d)\sum\limits_{i=1}^{\frac{n}{d}}miu(i)$

  $=\sum\limits_{d=1}^{n}g(d)S(\frac{n}{d})$
  所以说$g(1)S(n)=\sum\limits_{i=1}^{n}g(i)S(\frac{n}{i})-\sum\limits_{i=2}^{n}g(i)S(\frac{n}{i})$

       $=\sum\limits_{i=1}^{n}(g*miu)(i)-\sum\limits_{i=2}^{n}g(i)S(\frac{n}{i})$

  然后我们惊奇的发现,如果我们设g(x)=1

  因为1*miu=e(因为有个定理是$\sum\limits_{d|n}miu(d)=$  n=1时1,n>1时0)

  那么这玩意就变成了$S(n)=1-\sum\limits_{i=2}^{n}S(\frac{n}{d})$

  然后这个玩意可以先用线性筛求出$n^\frac{2}{3}$的前缀和,然后用这个表达式应用数论分块,递归搞搞就好了

  就问炫不炫

  

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<map>
#define maxn 5000000
using namespace std;
inline long long read(){
    long long num=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')    f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        num=num*10+ch-'0';
        ch=getchar();
    }
    return num*f;
}

int prime[maxn],tot;
bool s[maxn];
long long phi[maxn];
long long miu[maxn];
map<int,long long>_phi,_miu;

long long calcmiu(long long n){
    if(n<maxn)    return miu[n];
    if(_miu.count(n)) return _miu[n];
    long long x=2,ans=1;
    while(x<=n){
        long long y=n/(n/x);
        ans-=calcmiu(n/x)*(y-x+1);
        x=y+1;
    }
    return _miu[n]=ans;
}

long long calcphi(long long n){
    if(n<maxn)    return phi[n];
    if(_phi.count(n)) return _phi[n];
    long long x=2,ans=n*(n+1)/2;
    while(x<=n){
        long long y=n/(n/x);
        ans-=calcphi(n/x)*(y-x+1);
        x=y+1;
    }
    return _phi[n]=ans;
}

int main(){
    int T=read();
    s[1]=1;miu[1]=1;phi[1]=1;miu[0]=0;phi[0]=0;
    for(int i=2;i<maxn;++i){
        if(!s[i]){
            s[i]=1;
            phi[i]=i-1;
            miu[i]=-1;
            prime[++tot]=i;
        }
        for(int j=1;j<=tot&&prime[j]*i<maxn;++j){
            s[prime[j]*i]=1;
            if(i%prime[j]){
                miu[i*prime[j]]=-miu[i];
                phi[i*prime[j]]=phi[i]*(prime[j]-1);
            }
            else{
                phi[i*prime[j]]=phi[i]*prime[j];
                miu[i*prime[j]]=0;
                break;
            }
        }
    }
    for(int i=2;i<maxn;++i){
        phi[i]+=phi[i-1];
        miu[i]+=miu[i-1];
    }
    while(T--){
        long long n=read();
        printf("%lld %lld\n",calcphi(n),calcmiu(n));
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/cellular-automaton/p/8227802.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值