加载中...

杜教筛

pmKqGTS.png

P4213

杜教筛 \(O(n^{\frac{2}{3}})\)\(\sum_{i=1}^{n} \phi(n)\)\(\sum_{i=1}^{n} u(n)\)\(n \le 10^{9}\)

const int N = 2e6 + 5;
bool st[N];
int primes[N], tot, mu[N], phi[N];
ll s_mu[N], s_phi[N];
map<ll,ll> mp_smu, mp_sphi;

void sieve(int n){
    mu[1] = phi[1] = 1;
    for(int i = 2; i <= n; i ++){
        if(!st[i]){
            primes[++tot] = i;
            mu[i] = -1;
            phi[i] = i - 1;
        }
        for(int j = 1; j <= tot && i * primes[j] <= n; j ++){
            st[i * primes[j]] = true;
            if(i % primes[j] == 0){
                phi[i * primes[j]] = phi[i] * primes[j]; 
                break;
            }
            mu[i * primes[j]] = -mu[i];
            phi[i * primes[j]] = phi[i] * (primes[j] - 1); 
        }
    } 
}

ll Sphi(ll n){
    if(n <= (int)2e6) return s_phi[n];
    if(mp_sphi.count(n)) return mp_sphi[n];
    ll ans = n * (n + 1) / 2;
    for(ll l = 2, r; l <= n; l = r + 1){
        r = n / (n / l);
        ans -= Sphi(n / l) * (r - l + 1);
    }
    return mp_sphi[n] = ans;
}

ll Smu(ll n){
    if(n <= (int)2e6) return s_mu[n];
    if(mp_smu.count(n)) return mp_smu[n];
    ll ans = 1;
    for(ll l = 2, r; l <= n; l = r + 1){
        r = n / (n / l);
        ans -= Smu(n / l) * (r - l + 1);
    }
    return mp_smu[n] = ans;
}

void solve()
{
    int n;
    cin >> n;
    cout << Sphi(n) << " " << Smu(n) << '\n';
}

P3768

Q:给定 \(n,m\),求:

\[\sum_{i=1}^{n}\sum_{j=1}^{m} ij\gcd(i,j) \]

\(n \le 10^{10}\)

pmMYK9x.png

pmKZYOf.png
const int N = 5e6 + 5;

bool st[N];
int primes[N], tot, mu[N], phi[N];
ll s_phi[N];

ll n;
int p;

void sieve(int n){
    mu[1] = phi[1] = 1; // init
    for(int i = 2; i <= n; i ++){
        if(!st[i]){
            primes[++tot] = i;
            mu[i] = -1;
            phi[i] = i - 1;
        }
        for(int j = 1; j <= tot && primes[j] <= n / i; j ++){
            st[i * primes[j]] = true;
            if(i % primes[j] == 0){ // i 是 primes[j] 的倍数
                mu[i * primes[j]] = 0; // 质因子 primes[j] 至少有2个,mu 一定为 0
                phi[i * primes[j]] = phi[i] * primes[j]; 
                break;
            }
            // i 不是 primes[j] 的倍数时,i 与 primes[j] 互质,积性函数性质成立
            mu[i * primes[j]] = -mu[i]; // 任意质数的 mu 均为 -1
            phi[i * primes[j]] = phi[i] * (primes[j] - 1); // 任意质数 p 的 phi 均为 p - 1
        }
    } 

    for(int i = 1; i <= n; i ++){
        s_phi[i] = (s_phi[i - 1] + 1ll * i * i % p * phi[i] % p) % p;
    }
}

ll qpow(ll a, ll b, int p){
    ll res = 1;
    while(b){
        if(b & 1) res = res * a % p;
        b >>= 1;
        a = a * a % p;
    }
    return res;
}

int inv6;
ll S2(ll x){
    return LL(1) * x * (x + 1) % p * (2 * x + 1) % p * inv6 % p;
}
ll S3(ll x){
    return (LL(1) * x * (x + 1) / 2) % p * (LL(1) * x * (x + 1) / 2) % p;
}
map<ll,ll> mp;
ll Sn(ll x){
    if(x <= (int)5e6) return s_phi[x];
    if(mp.count(x)) return mp[x];
    ll res = S3(x);
    for(ll l = 2, r; l <= x; l = r + 1){
        r = x / (x / l);
        res = (res - (S2(r) - S2(l - 1) + p) % p * Sn(x / l) % p + p) % p;
    }
    return mp[x] = res;
}

void solve()
{
    cin >> p >> n;
    sieve(5e6);
    inv6 = qpow(6, p - 2, p);
    ll ans = 0;
    for(ll l = 1, r; l <= n; l = r + 1){
        r = n / (n / l);
        ans = (ans + (Sn(r) - Sn(l - 1) + p) % p * S3(n / l) % p) % p;
    }
    cout << ans << "\n";
}

复杂度分析:主函数内是整除分块套杜教筛。单次调用杜教筛的复杂度为 \(O(n^{\frac{2}{3}})\),整除分块调用 \(O(\sqrt n)\) 次,但是不能直接把二者相乘作为最终的复杂度,因为当 \(n\) 很小时,杜教筛可以直接返回已计算结果。很难精确地估算本题复杂度,但是能过qwq...

posted @ 2026-06-12 16:30  小橘奏  阅读(5)  评论(0)    收藏  举报