Luogu4213 【模板】杜教筛(Sum)

https://www.luogu.com.cn/problem/P4213

杜教筛

杜教筛核心公式:

\[g(1)S(n)=\sum_{i=1}^n (f*g)(i)-\sum_{i=2}^n g(i)\times S(\lfloor \frac{n}{i} \rfloor) \\ S(n)为答案,g和(f*g)均为易求得的函数,\\ S(\lfloor \frac{n}{i} \rfloor)可以数论分块,并递归求解(记忆化搜索) \]

\(Part 1:\mu\)

\[根据\mu*I=\epsilon \\ f=\mu,g=I,(f*g)=\epsilon \\ 代入求解\\ S(n)=1-\sum_{i=2}^n S(\lfloor \frac{n}{i} \rfloor) \]

\(Part 2:\varphi\)

\[根据\varphi*I=id \\ f=\varphi,g=I,(f*g)=id \\ 代入求解\\ S(n)=\frac{n(n+1)}{2}-\sum_{i=2}^n S(\lfloor \frac{n}{i} \rfloor) \]

\(C++ Code:\)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<map>
#define ll long long
#define N 5000000
using namespace std;
int T;
ll prime[N];
bool pri[N];
int cnt;
ll n,phi[N+10],mu[N+10],sphi[N+10],smu[N+10];
map<ll,ll>ansmu,ansphi;//C++11开unordered_map更快
void Pre()//预处理,优化复杂度
{
    phi[1]=mu[1]=1;//开始处理欧拉函数和莫比乌斯函数
    for (ll i=2;i<=N;i++)
    {
        if (!pri[i])
            prime[++cnt]=i,phi[i]=i-1,mu[i]=-1;
        for (ll j=1;j<=cnt;j++)
        {
            ll g=i*prime[j];
            if (g>N)
                break;
            pri[g]=true;
            if (i%prime[j]==0)
            {
                phi[g]=phi[i]*prime[j];
                mu[g]=0;
                break;
            }
            phi[g]=phi[i]*phi[prime[j]];
            mu[g]=mu[i]*mu[prime[j]];
        }
    }
    for (ll i=1;i<=N;i++)//前缀和
    {
        sphi[i]=sphi[i-1]+phi[i];
        smu[i]=smu[i-1]+mu[i];
    }
}
ll ans1(ll n)//莫比乌斯函数求解
{
    if (n<=N)
        return smu[n];
    if (ansmu[n])
        return ansmu[n];
    ll l,r;
    ll ans=1;
    for (l=2;l<=n;l=r+1)
    {
       r=n/(n/l);
       ans-=(r-l+1)*ans1(n/l);
    }
    return ansmu[n]=ans;
}
ll ans2(ll n)//欧拉函数求解
{
    if (n<=N)
        return sphi[n];
    if (ansphi[n])
        return ansphi[n];
    ll l,r;
    ll ans=(n+1)*n/2;
    for (l=2;l<=n;l=r+1)
    {
       r=n/(n/l);
       ans-=(r-l+1)*ans2(n/l);
    }
    return ansphi[n]=ans;
}
int main()
{
    Pre();
    scanf("%d",&T);
    while (T --> 0)
    {
        scanf("%lld",&n);
        printf("%lld %lld\n",ans2(n),ans1(n));
    }
}
posted @ 2020-07-21 17:41  GK0328  阅读(82)  评论(0编辑  收藏  举报