bzoj2820: YY的GCD 莫比乌斯反演

bzoj2820: YY的GCD

Description

神犇YY虐完数论后给傻×kAc出了一题给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对kAc这种
傻×必然不会了,于是向你来请教……多组输入

Input

第一行一个整数T 表述数据组数接下来T行,每行两个正整数,表示N, M

Output

T行,每行一个整数表示第i组数据的结果

Sample Input

2
10 10
100 100

Sample Output

30
2791

HINT

T = 10000
N, M <= 10000000

分析

解法1:瞎推
假设

n<m

ans=isprime(p)i=1nj=1m[gcd(i,j)==p]

=isprime(p)i=1npi=1mp[gcd(i,j)==1]

=isprime(p)i=1npi=1mpd|gcd(i,j)gcd(i,j)μ(d)

=isprime(p)d=1npμ(d)npdmpd

k=pd
=k=1nisprime(p)p|kμ(kp)nkmk

=nk=1F(k)nkmk
对于 F(k) 可以进行线性筛预处理
对于 nkmk 用神奇的下底函数分块可以达到 O(n)
解法2:
gcd(i,j)==d 不好求,我们望莫比乌斯反演上靠,考虑f的约束和和倍数和,发现
F(d)=d|pf(p)

f(p)=injm[gcd(i,j)==p]

F(d)=injmd|gcd(i,j)=ndmd

由莫比乌斯反演得
f(p)=p|dμ(dp)ndmd

ans=isprime(p)i=1nj=1m[gcd(i,j)==p]

=isprime(p)i=1npi=1mp[gcd(i,j)==1]

=isprime(p)f(1)

=isprime(p)d=1npμ(d)npdmpd

剩下的和上面的操作时一样滴

模型

i=1nj=1m[gcd(i,j)==d]=k=1ndμ(k)nkdmkd

还有关于
pμ(kp)
的线性筛法处理
好题好题

代码

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 11000000;
int read()
{
    char ch = getchar(); int x = 0, f = 1;
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}
int miu[N + 10], pri[N + 10], f[N + 10], n, top;
bool vis[N + 10];

int get_miu() {
    miu[1] = 1;
    for(int i = 2;i <= N; ++i) {
        if(!vis[i]) {
            miu[i] = -1;
            pri[++top] = i;
        }
        for(int j = 1;j <= top; ++j) {
            if(pri[j] * i > N) break;
            vis[pri[j] * i] = true;
            if(!(i % pri[j])) {
                miu[pri[j] * i] = 0;
                break;
            }
            else miu[pri[j] * i] = -miu[i];
        }
    }
    for(int i = 1;i <= top; ++i)
        for(int j = 1;j * pri[i] <= N; ++j)
            f[j * pri[i]] += miu[j];
    for(int i = 1;i <= N; ++i) f[i] += f[i - 1];
}

long long cal(int n, int m) {
    if(n > m) swap(n, m); long long ret = 0;
    for(int i = 1, pos;i <= n; i = pos + 1) {
        pos = min(n / (n / i), m / (m / i));
        ret += 1LL * (f[pos] - f[i - 1]) * (n / i) * (m / i);
    }
    return ret;
}

int main() {
    get_miu();
    int T = read();
    while(T--) printf("%lld\n", cal(read(), read()));
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值