杜教筛

作用

求部分积性函数前缀和,将一个不太好求的前缀和转化为两个比较好求的前缀和。

式子

\[S(n)=\sum_{i=1}^n f(i) \]

\[g(1)S(n)=\sum_{i=1}^ng(i)S(\lfloor\frac ni\rfloor)-\sum_{i=2}^ng(i)S(\lfloor\frac ni\rfloor) \]

\[\because \sum_{i=1}^n(f*g)(i)=\sum_{i=1}^n\sum_{d|i}f(d)g(\frac id)=\sum_{d=1}^ng(d)\sum_{i=1}^{\lfloor\frac nd\rfloor}f(i)=\sum_{d=1}^ng(d)S(\lfloor\frac nd\rfloor) \]

\[\therefore g(1)S(n)=\sum_{i=1}^n(f*g)(i)-\sum_{i=2}^ng(i)S(\lfloor\frac ni\rfloor) \]

过程

找到一个合适的积性函数 \(g\),使得能够快速求出 \(\sum g(i)\)\(\sum(f*g)(i)\),那么就可以套用上述式子求出 \(f\) 的前缀和。复杂度 \(O(n^{\frac23})\)

所以需要记住一些狄利克雷卷积关系

  • \(\mu*I=\epsilon\)
  • \(\mu*id=\phi\)
  • \(\phi*I=id\)

例子

\(\sum\mu(i)\)

找到 \(\mu*I=\epsilon\),其中 \(\sum I(i)=n\)\(\sum\epsilon(i)=1\)

\(\sum\phi(i)\)

找到 \(\phi*I=id\),其中 \(\sum I(i)=n\)\(\sum id=\frac{n(n+1)}2\)

代码

求上述两个东西。

注意我们要保存 \(\lfloor\frac ni\rfloor\) 这些位置的 \(S\),所以要建立一个映射关系

\[i\to\begin{cases}i&i\le\sqrt n\\\sqrt n+\lfloor\frac ni\rfloor&i>\sqrt n\end{cases} \]

所以数组只用开 \(2\sqrt n\)

实际上小于1e7的部分可以线性筛

#include<bits/stdc++.h>
using namespace std;
   
template<typename T>
inline void Read(T &n){
    char ch; bool flag=false;
    while(!isdigit(ch=getchar()))if(ch=='-')flag=true;
    for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48));
    if(flag)n=-n;
}

#define int long long
typedef long long ll;

enum{
    MAXN = 10000000,
    M = 46341
};

int prime[MAXN], pnum;
ll mu[MAXN+1], phi[MAXN+1];
char check[MAXN+1];
inline void Sieve(int n){
    for(int i=2; i<=n; i++){
        if(!check[i]) prime[++pnum] = i, mu[i] = -1, phi[i] = i-1;
        for(int j=1; i*prime[j]<=n and j<=pnum; j++){
            int nxt = i*prime[j];
            check[nxt] = true;
            if(i%prime[j]==0){
                phi[nxt] = phi[i]*prime[j];
                break;
            }
            else
                phi[nxt] = phi[i]*(prime[j]-1),
                mu[nxt] = -mu[i];
        }
    }
    mu[1] = phi[1] = 1;
    for(int i=1; i<=n; i++) mu[i] += mu[i-1], phi[i] += phi[i-1];
}

int n;
ll smu[M+1], sphi[M+1];

char vis[M+1];
ll getmu(int x){
    if(x<=MAXN) return mu[x];
    if(vis[n/x]) return smu[n/x];
    vis[n/x] = true;
    ll &res = smu[n/x] = 1;
    for(int l=2, r; l<=x; l=r+1){
        r = x/(x/l);
        res -= 1ll*(r-l+1)*getmu(x/l);
    }
    return res;
}

ll getphi(int x){
    if(x<=MAXN) return phi[x];
    if(vis[n/x]) return sphi[n/x];
    vis[n/x] = true;
    ll &res = sphi[n/x] = 1ll*x*(x+1)/2;
    for(int l=2, r; l<=x; l=r+1){
        r = x/(x/l);
        res -= 1ll*(r-l+1)*getphi(x/l);
    }
    return res;
}

signed main(){
    Sieve(MAXN);
    int T; Read(T);
    while(T--){
        Read(n);
        memset(vis,false,sizeof vis);
        printf("%lld ",getphi(n));
        memset(vis,false,sizeof vis);
        printf("%lld\n",getmu(n));
    }
    return 0;
}
posted @ 2021-06-25 12:00  oisdoaiu  阅读(43)  评论(0编辑  收藏  举报