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 <bits/stdc++.h>
#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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值