P4213 【模板】杜教筛(杜教筛)题解

题意:

(sum_{i=1}^nvarphi(i))(sum_{i=1}^nmu(i))

思路:

由性质可知:(mu*I=epsilon,varphi*I=id)那么可得:

[S_{varphi}(n)=sum_{i=1}^nvarphi(i)=frac{(n+1)n}{2}-sum_{i=2}^nS_{varphi}(lfloorfrac{n}{i} floor)\ S_{mu}(n)sum_{i=1}^nmu(i)=1-sum_{i=2}^nS_{mu}(lfloorfrac{n}{i} floor) ]

然后用现预处理一部分答案,然后数论分块其他答案,用记忆化记录中间答案。
很卡常...

还有一种不用(map)的方法,详见代码(2)

代码:

#include<map>
#include<set>
#include<cmath>
#include<cstdio>
#include<stack>
#include<ctime>
#include<vector>
#include<queue>
#include<cstring>
#include<string>
#include<sstream>
#include<iostream>
#include<algorithm>
#include<unordered_map>
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int maxn = 6000000 + 5;
const ll MOD = 998244353;
const ull seed = 131;
const int INF = 0x3f3f3f3f;

int vis[maxn];
int prime[maxn], cnt;
int N = 6000000;
ll mu[maxn], phi[maxn];
unordered_map<int, int> mmu;
unordered_map<int, ll> mphi;
void init(int n){
    cnt = 0;
    mu[1] = 1;
    phi[1] = 1;
    for(int i = 2; i <= n; i++){
        if(!vis[i]){
            prime[cnt++] = i;
            mu[i] = -1;
            phi[i] = i - 1;
        }
        for(int j = 0; j < cnt && prime[j] * i <= n; j++){
            vis[i * prime[j]] = 1;
            if(i % prime[j] == 0){
                mu[i * prime[j]] = 0;
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            }
            mu[i * prime[j]] = -mu[i];
            phi[i * prime[j]] = phi[i] * (prime[j] - 1);
        }
    }

    for(int i = 2; i <= n; i++){
        phi[i] += phi[i - 1];
        mu[i] += mu[i - 1];
    }
}

ll Sphi(int n){
    if(n <= N) return phi[n];
    if(mphi.count(n)) return mphi[n];
    ll ans = 1LL * n * (1 + n) >> 1LL;
    for(int l = 2, r; l <= n; l = r + 1){
        r = min(n / (n / l), n);
        ans -= (r - l + 1) * Sphi(n / l);
    }
    return mphi[n] = ans;
}
int Smu(int n){
    if(n <= N) return mu[n];
    if(mmu.count(n)) return mmu[n];
    int ans = 1;
    for(int l = 2, r; l <= n; l = r + 1){
        r = min(n / (n / l), n);
        ans -= (r - l + 1) * Smu(n / l);
    }
    return mmu[n] = ans;
}

int main(){
    init(N);
    int T;
    scanf("%d", &T);
    while(T--){
        int n;
        scanf("%d", &n);
        printf("%lld %d
", Sphi(n), Smu(n));
    }
    return 0;
}

#include<map>
#include<set>
#include<cmath>
#include<cstdio>
#include<stack>
#include<ctime>
#include<vector>
#include<queue>
#include<cstring>
#include<string>
#include<sstream>
#include<iostream>
#include<algorithm>
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int maxn = 6000000 + 5;
const ll MOD = 998244353;
const ull seed = 131;
const int INF = 0x3f3f3f3f;
int vis[maxn];
int prime[maxn], cnt;
int N = 6000000;
ll phi[maxn], mphi[100000];
int mu[maxn], mmu[100000];
int vis2[100000];
int n, up;
void init(int n){
    cnt = 0;
    mu[1] = 1;
    phi[1] = 1;
    for(int i = 2; i <= n; i++){
        if(!vis[i]){
            prime[cnt++] = i;
            mu[i] = -1;
            phi[i] = i - 1;
        }
        for(int j = 0; j < cnt && prime[j] * i <= n; j++){
            vis[i * prime[j]] = 1;
            if(i % prime[j] == 0){
                mu[i * prime[j]] = 0;
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            }
            mu[i * prime[j]] = -mu[i];
            phi[i * prime[j]] = phi[i] * (prime[j] - 1);
        }
    }

    for(int i = 2; i <= n; i++){
        phi[i] += phi[i - 1];
        mu[i] += mu[i - 1];
    }
}
int getmu(int x){
    return x <= N? mu[x] : mmu[up / x];
}
ll getphi(int x){
    return x <= N? phi[x] : mphi[up / x];
}
void solve(int n){
    int t = up / n;
    if(n <= N) return;
    if(vis2[t]) return;
    vis2[t] = 1;
    mmu[t] = 1, mphi[t] = 1LL * n * (n + 1) / 2;
    for(int l = 2, r; l <= n; l = r + 1){
        r = n / (n / l);
        solve(n / l);
        mmu[t] -= (r - l + 1) * getmu(n / l);
        mphi[t] -= (r - l + 1) * getphi(n / l);
    }
}

int main(){
    init(N);
    int T;
    scanf("%d", &T);
    while(T--){
        scanf("%d", &n);
        up = n;
        if(n <= N) printf("%lld %d
", phi[n], mu[n]);
        else{
            memset(vis2, 0, sizeof(vis2));
            solve(n);
            printf("%lld %d
", mphi[1], mmu[1]);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/KirinSB/p/11461267.html