杜教筛学习笔记

模板题

洛谷P4213 【模板】杜教筛(Sum)

前置知识

常见积性函数的特性

杜教筛

杜教筛就是在非线性时间内求出积性函数前缀和的一种算法

现在要求\(S(n)=\sum\limits_{i=1}^nf(i)\)的值

其中f(i)是积性函数,n很大以至于O(n)并不能通过

考虑一个积性函数\(g(x)\),求\((f\times g)(x)\)的前缀和
\[ \therefore \sum\limits_{i=1}^n(f\times g)(i)\\ =\sum\limits_{i=1}^n\sum\limits_{d|i}f(\frac id)g(d)\\ =\sum\limits_{d=1}^ng(d)\sum\limits_{i=1}^{\lfloor\frac nd\rfloor}f(i)\\ =\sum\limits_{d=1}^ng(d)S(\lfloor\frac nd\rfloor)\\ \]
因为g是积性函数,所以g(1)=1,所以S(n)=g(1)S(n)
\[ \therefore S(n)=g(1)S(n)\\ =\sum\limits_{i=1}^ng(i)S(\lfloor\frac ni\rfloor)-\sum\limits_{i=2}^ng(i)S(\lfloor\frac ni\rfloor)\\ =\sum\limits_{i=1}^n(f\times g)(i)-\sum\limits_{i=1}^ng(i)S(\lfloor\frac ni\rfloor) \]
其中\(\sum\limits_{i=1}^n(f\times g)(i)\)\(f\)\(\mu\),\(\varphi\),\(\varphi \times id\)时,取\(g\)\(I\),则可以O(1)求出其值

分别为\(1\),\(\frac{n(n+1)}{2}\)\(\frac{n(n+1)(2n+1)}6\)

其中\(\sum\limits_{i=1}^ng(i)S(\lfloor\frac ni\rfloor)\)可以用数论分块求出

总时间复杂度\(O(n^{\frac 34})\)

如果先预处理出\(S(1)到S(m)\),则时间复杂度可以优化到\(O(\frac n{\sqrt m})\),证明略

Code

#include<bits/stdc++.h>
#define N 5000000
#define ll long long
using namespace std;
ll mu[N+varphiphi[N+5],prim[N+5],tot;
bool apr[N+5];
void pre(int n){
    mu[varphiphi[1]=1;
    for(int i=2;i<=n;i++){
        if(!apr[i])prim[++tot]=i,mu[i]=varphiphi[i]=i-1;
        for(int j=1;j<=tot&&prim[j]*i<=n;j++){
            apr[i*prim[j]]=1;
            if(i%prim[j]){
                mu[i*prim[j]]=-mu[i];
             varphiphi[i*prim[jvarphiphi[varphiphi[prim[j]];
            }else{
                mu[i*prim[j]]=0;
             varphiphi[i*prim[jvarphiphi[i]*prim[j];
                break;
            }
        }
    }
    for(int i=2;i<=n;i++)mu[i]+=mu[i-varphiphi[ivarphiphi[i-1];
}
map<int,ll>mu_;
ll SumMu(int n){
    if(n<=N)return mu[n];
    if(mu_[n])return mu_[n];
    ll re=0;
    for(int l=2,r=0;r<0x7fffffff&&l<=n;l=r+1){
        r=n/(n/l);
        re+=1ll*(r-l+1)*SumMu(n/l);
    }
    return mu_[n]=1ll-re;
}
map<int,varphiphi_;
ll varphiPhi(int n){
    if(n<=N)retuvarphiphi[n];
    varphiphi_[n])retuvarphiphi_[n];
    ll re=0;
    for(int l=2,r=0;r<0x7fffffff&&l<=n;l=r+1){
        r=n/(n/l);
        re+=1ll*(r-l+1)*varphiPhi(n/l);
    }
    retuvarphiphi_[n]=1ll*n*(n+1ll)/2ll-re;
}
signed main(){
    pre(N);
    int t,n;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        printf("%lld %lld\n",varphiPhi(n),SumMu(n));
    }
    return 0;
}
/*
6
1
2
8
13
30
2333

*/
posted @ 2019-05-10 11:28 The_KOG 阅读(...) 评论(...) 编辑 收藏