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

题意:

\(\sum_{i=1}^n\varphi(i)\)\(\sum_{i=1}^n\mu(i)\)

思路:

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

\[S_{\varphi}(n)=\sum_{i=1}^n\varphi(i)=\frac{(n+1)n}{2}-\sum_{i=2}^nS_{\varphi}(\lfloor\frac{n}{i}\rfloor)\\ S_{\mu}(n)\sum_{i=1}^n\mu(i)=1-\sum_{i=2}^nS_{\mu}(\lfloor\frac{n}{i}\rfloor) \]

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

还有一种不用\(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\n", 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\n", phi[n], mu[n]);
        else{
            memset(vis2, 0, sizeof(vis2));
            solve(n);
            printf("%lld %d\n", mphi[1], mmu[1]);
        }
    }
    return 0;
}

posted @ 2019-09-04 20:00  KirinSB  阅读(158)  评论(0编辑  收藏  举报