1. 程式人生 > >luoguP4213 [模板]杜教篩

luoguP4213 [模板]杜教篩

unsigned for () def -- std oid inline urn

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; 
}

luoguP4213 [模板]杜教篩