luoguP4213 [模板]杜教筛

https://www.luogu.org/problemnew/show/P4213

同 bzoj3944

考虑用杜教筛求出莫比乌斯函数前缀和,第二问随便过,第一问用莫比乌斯反演来做,中间的整除分块里的莫比乌斯前缀和刚好用第二问来做

杜教筛的时候先线性筛出前 N 个数的莫比乌斯函数前缀和,其余的用 map 记忆化搜索,实测 N 取 3670000 最佳(其实我只测了3次)

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

typedef unsigned long long ull;
typedef long long ll;

template <typename _T>
inline void read(_T &f) {
    f = 0; _T fu = 1; char c = getchar();
    while(c < '0' || c > '9') {if(c == '-') fu = -1; c = getchar();}
    while(c >= '0' && c <= '9') {f = (f << 3) + (f << 1) + (c & 15); c = getchar();}
    f *= fu;
}

const int N = 3670000;

map <ll, ll> val;
int mu[N], pri[N], isp[N], s[N], len = 0;
int T, n;

void Mu() {
    mu[1] = 1;
    for(int i = 2; i <= N - 10; i++) {
        if(!isp[i]) {pri[++len] = i; mu[i] = -1;}
        for(int j = 1; j <= len && i * pri[j] <= (N - 10); j++) {
            isp[i * pri[j]] = 1;
            if(i % pri[j] == 0) break;
            mu[i * pri[j]] = -mu[i];
        }
    }
    for(int i = 1; i <= N - 10; i++) s[i] = s[i - 1] + mu[i];
}

ll getmu(ll n) {
    if(n <= N - 10) return (ll)s[n];
    if(val.count(n)) return val[n];
    ll ans = 0;
    for(ll l = 2, r; l <= n; l = r + 1) {
        r = n / (n / l);
        ans += (r - l + 1) * getmu(n / l);
    }
    val[n] = 1 - ans;
    return val[n];
}

ll getphi(ll n) {
    ll ans = 0;
    for(ll l = 1, r; l <= n; l = r + 1) {
        r = n / (n / l);
        ans += (getmu(r) - getmu(l - 1)) * (n / l) * (n / l);
    }
    return (ans + 1) >> 1;
}

int main() {
    read(T); Mu();
    while(T--) {
        read(n);
        printf("%lld %lld\n", getphi(n), getmu(n));
    }
    return 0; 
}
posted @ 2018-08-24 22:23  LJC00118  阅读(152)  评论(0编辑  收藏  举报
/*
*/