P2522 HAOI2011 Problem b [莫比烏斯反演,數論分塊]

P2522 HAOI2011

題意

對於給出的n個詢問,每次求有多少個數對(x,y)(x,y),滿足axba≤x≤bcydc≤y≤d,且gcd(x,y)=kgcd(x,y) = kgcd(x,y)gcd(x,y)函數爲xxyy的最大公約數.

題解

即求式子x=aby=cd[gcd(x,y)=k]\sum_{x=a}^b\sum_{y=c}^d[gcd(x,y)=k].

f(n,m)=x=1ny=1m[gcd(x,y)=k]f(n,m)=\sum_{x=1}^n\sum_{y=1}^m[gcd(x,y)=k]

根據二維前綴和公式,可以將式子轉換成:

x=aby=cd[gcd(x,y)=k]=f(b,d)+f(a1,c1)f(a1,d)f(b,c1)\sum_{x=a}^b\sum_{y=c}^d[gcd(x,y)=k]=f(b,d)+f(a-1,c-1)-f(a-1,d)-f(b,c-1)

因此我們只要能得到f(n,m)f(n,m)的計算方法即可.

f(n,m)f(n,m)的套路非常明顯:莫比比烏斯反演

由於kdx=1ny=1m[gcd(x,y)=d]=nkmk\sum_{k|d}\sum_{x=1}^n\sum_{y=1}^m[gcd(x,y)=d] = \lfloor \frac{n}{k} \rfloor \lfloor \frac{m}{k} \rfloor.

反演得到f(n,m)=kdμ(dk)ndmdf(n,m)=\sum_{k|d}\mu(\frac{d}{k})\lfloor \frac{n}{d} \rfloor \lfloor \frac{m}{d} \rfloor

t=d/kt = d/k.

f(n,m)=t=1n/dμ(t)nktmktf(n,m)=\sum_{t=1}^{n/d}\mu(t)\lfloor \frac{n}{kt} \rfloor \lfloor \frac{m}{kt} \rfloor

對上式子進行分塊計算,可以將時間複雜度從O(n)O(n)降至O(n)O(\sqrt{n}).

總結

對於形如f(x)=xdμ(dx)g(nd)f(x)=\sum_{x|d}\mu(\frac{d}{x})g(\lfloor \frac{n}{d} \rfloor)這樣的式子,我們都可以用t=d/xt=d/x代換後數論分塊進行加速.

f(x)=t=1n/xμ(t)g(nxt)f(x)=\sum_{t=1}^{n/x}\mu(t)g(\lfloor \frac{n}{xt} \rfloor)

時間複雜度從O(n)O(n)降至O(n)O(\sqrt{n}).

代碼

#include <iostream>
#include <algorithm>
#include <cstring>
#define pr(x) std::cout << #x << ':' << x << std::endl
#define rep(i,a,b) for(int i = a;i <= b;++i)

const int N = 50000;

int prime[N+10],zhi[N+10],mu[N+10],pcnt;

void sieve() {
    zhi[1] = mu[1] = 1;
    for(int i = 2;i <= N;++i) {
        if(!zhi[i]) {
            mu[i] = -1;
            prime[pcnt++] = i;
        }
        for(int j = 0;j < pcnt && prime[j]*i <= N;++j) {
            zhi[i*prime[j]] = 1;
            if(i % prime[j] == 0) {
                mu[i*prime[j]] = 0;
                break;
            }
            else 
                mu[i*prime[j]] = -mu[i];
        }
    }
    for(int i = 1;i <= N;++i) mu[i] += mu[i-1];
}
int a,b,c,d,k,T;
int calc(int n,int m) {
    int ans = 0;
    int lim = std::min(n/k,m/k);
    for(int i = 1,nx1,nx2,nxt;i <= lim;i=nxt+1) {
        nx1 = n/(n/i);
        nx2 = m/(m/i);
        nxt = nx1>nx2?nx2:nx1;
        ans += (mu[nxt]-mu[i-1])*(n/i/k)*(m/i/k);
    }
    return ans;
}
int main() {
    std::ios::sync_with_stdio(false);
    sieve();
    std::cin >> T;
    while(T--) {
        std::cin >> a >> b >> c >> d >> k;
        int ans = calc(b,d)+calc(a-1,c-1)-calc(a-1,d)-calc(b,c-1);
        std::cout << ans << std::endl;
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章