模板题,求欧拉函数和莫比乌斯函数的前缀和

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

学习粗:https://blog.csdn.net/weixin_43914593/article/details/104229700

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 5e6+7; //超过n^2/3,够大了

int prime[maxn];   //记录质数
bool vis[maxn];    //记录是否被筛;
int mu[maxn];                   //莫比乌斯函数值
ll phi[maxn];                   //欧拉函数值
unordered_map<int,int> summu;   //莫比乌斯函数前缀和
unordered_map<int,ll> sumphi;   //欧拉函数前缀和
inline void init(){             //线性筛预计算一部分答案
    int cnt = 0;
    vis[0] = vis[1] = 1;
    mu[1] = phi[1] = 1;
    for(int i=2;i<maxn;i++){
        if(!vis[i]){
            prime[cnt++] = i;
            mu[i] = -1;
            phi[i] = i-1;
        }
        for(int j=0;j<cnt && i*prime[j]<maxn;j++){
            vis[i*prime[j]] = 1;
            if(i%prime[j]){
                mu[i*prime[j]] = -mu[i];
                phi[i*prime[j]] = phi[i]*phi[prime[j]];
            }
            else{
                mu[i*prime[j]] = 0;
                phi[i*prime[j]] = phi[i]*prime[j];
                break;
            }
        }
    }
    for(int i=1;i<maxn;i++){  //最后,mu[]和phi[]改为记录1~maxn的前缀和。
        mu[i] += mu[i-1];
        phi[i] += phi[i-1];
    }
}
inline int gsum(int x){         // g(i)的前缀和
    return x;
}
inline int getsmu(int x){
    if(x < maxn) return mu[x];   //预计算
    if(summu[x]) return summu[x];  //记忆化
    int ans = 1;                //杜教筛公式中的 1
    for(int l=2,r;l<=x;l=r+1){  //用整除分块计算杜教筛公式
        r = x/(x/l);
        ans -= (gsum(r)-gsum(l-1))*getsmu(x/l);
    }
    return summu[x] = ans/gsum(1);
}
inline ll getsphi(int x){
    if(x < maxn) return phi[x];
    if(sumphi[x]) return sumphi[x];  //记忆化,每个sumphi[x]只用算一次
    ll ans = 1LL*x*(x+1)/2;      //杜教筛公式中的 n(n+1)/2
    for(int l=2,r;l<=x;l=r+1){   //用整除分块计算杜教筛公式,这里算 sqrt(x)次
        r = x/(x/l);
        ans -= (gsum(r)-gsum(l-1))*getsphi(x/l);
    }
    return sumphi[x] = ans/gsum(1);
}
int main(){
    init();  //用线性筛预计算一部分
    int t;
    scanf("%d",&t);
    while(t--){
        int n;
        scanf("%d",&n);
        printf("%lld %d\n",getsphi(n),getsmu(n));
    }
    return 0;
}
View Code

 

posted @ 2020-06-01 22:31  starve_to_death  阅读(213)  评论(0编辑  收藏  举报