[BZOJ2820]YY的GCD

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

Solution

假定n<m
isprime(p)a=1nb=1mgcd(a,b)==p

isprime(p)a=1npb=1mpgcd(a,b)==1
isprime(p)a=1npb=1mpd|gcd(a,b)μ(d)
isprime(p)a=1npb=1mpd|ad|bμ(d)

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

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

k=1nF(k)nkmk

可以线筛预处理F,或者暴力枚举质数(这个复杂度我不能确定),按照素数粗略个数n/logn以及调和级数求和nlogn来看暴力的复杂度接近On
处理完F以后就是喜闻乐见的下底函数分块

Code

#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, r, l) for (int i = (r); i >= (l); i--)
#define X first
#define Y second
#define MS(_) memset(_, 0, sizeof(_))
#define PB push_back
#define MP make_pair
#define debug(...) fprintf(stderr, __VA_ARGS__)
template<typename T> inline void read(T &x){
    x = 0; T f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while (isdigit(ch))  {x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}
typedef long long ll;
const int N = 11111111;

bool check[N];
int prime[N];
ll mu[N], f[N], sum[N];
inline void prework(){ int tot = 0;
    MS(check); mu[1] = f[1] = 1ll;
    rep(i, 2, N){
        if (!check[i]){prime[++tot] = i; mu[i] = -1; f[i] = 1ll;}
        rep(j, 1, tot){
            if (1ll * i * prime[j] > 1ll * N) break;
            check[i * prime[j]] = true;
            if (i % prime[j] == 0){
                mu[i * prime[j]] = 0; f[i * prime[j]] = mu[i]; break;
            }else{
                mu[i * prime[j]] = -mu[i]; f[i * prime[j]] = mu[i] - f[i];
            }
        }
    }
    rep(i, 1, N) sum[i] = sum[i - 1] + f[i] * 1ll;
}

inline ll cal(ll n, ll m){
    ll ans = 0; int pos = 0;
    for (int i = 2; i <= n; i = pos + 1){
        pos = min(n/(n/i), m/(m/i));
        ans += (sum[pos] - sum[i-1]) * (n/i) * (m/i) * 1ll;
    }
    return ans;
}
int main(){
    prework();
    int case_cnt; read(case_cnt);
    while (case_cnt--){ ll n, m;
        read(n); read(m); if (n > m) swap(n, m);
        printf("%lld\n", cal(n, m));
    }
    return 0;
}

你可能感兴趣的:(数学,莫比乌斯反演)