zoukankan      html  css  js  c++  java
  • 模板题,求欧拉函数和莫比乌斯函数的前缀和

    题: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
    ",getsphi(n),getsmu(n));
        }
        return 0;
    }
    View Code
  • 相关阅读:
    Cpp Chapter 12: Classes and Dynamic Memory Allocation Part1
    Cpp Chapter 11: Working with Classes Part2
    Cpp Chapter 11: Working with Classes Part1
    Cpp Chapter 10: Objects and Classes Part2
    摄影技术学习
    安装texlive2017(latex的编译软件)
    文献管理工具的使用(Mendeley和Endnote)
    函数的级数展开和渐近展开
    常用英语语法小结
    常微分方程
  • 原文地址:https://www.cnblogs.com/starve/p/13027979.html
Copyright © 2011-2022 走看看