BZOJ 2820: YY的GCD(莫比乌斯反演)

题目描述

传送门

题目大意:求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对。多组数据。

T = 10000
N, M <= 10000000


思路

最近沉迷刷数论水题,弱得瑟瑟发抖~

枚举质数 p ,套路反演一波

ans=pi=1npμ(i)npimpi

枚举 pi

ans=T=1nnTmTp|Tμ(Tp)

F(T)=p|Tμ(Tp) ,注意前提是 p 是质数。

考虑如何求 F(T) 的前缀和。好像不能筛了,但是 [1,n] 的质数好像只有 nln(n) 个。我们直接枚举质数和其倍数计算 F(T) 然后求前缀和就好了。

由于调和级数的性质, ni=1ninln(n) ,对于每个 i 均摊 ln(n) 。所以预处理 F(T) 前缀和的时间就是 O(nln(n))O(ln(n))=O(n)

询问时分一下块,可以做到 O(Tn)


代码

#include 
#define maxn 10000010
#define temp (i * prime[j])

using namespace std;

typedef long long LL;

int T, n, m, cnt;
int prime[maxn];
bool Vis[maxn];
LL miu[maxn], F[maxn];

void Da(){
    miu[1] = 1LL;
    for(int i = 2; i < maxn; i++){
        if(!Vis[i]){
            prime[++cnt] = i;
            miu[i] = -1LL;
        }
        for(int j = 1; j <= cnt && temp < maxn; j++){
            Vis[temp] = true;
            if(i % prime[j] == 0){
                miu[temp] = 0LL;
                break;  
            }
            else  miu[temp] = -miu[i];
        }
    }
    for(int j = 1; j <= cnt; j++)
        for(int i = 1; temp < maxn; i++)
            F[temp] += miu[i];

    for(int i = 2; i < maxn; i++)  F[i] += F[i-1];
}

LL Solve(){
    if(n > m)  swap(n, m);
    LL ans = 0LL;
    int last;

    for(int i = 1; i <= n; i = last+1){
        last = min(n/(n/i), m/(m/i));
        ans += 1LL * (n/i) * (m/i) * (F[last] - F[i-1]);
    }
    return ans;
}

int main(){

    scanf("%d", &T);

    Da();

    while(T --){
        scanf("%d%d", &n, &m);
        printf("%lld\n", Solve());
    }

    return 0;
}

你可能感兴趣的:(数论,&,数学,莫比乌斯反演,&,杜教筛,BZOJ)