Bzoj 2820: YY的GCD

Bzoj 2820: YY的GCD

膜一发rqy
真正的学习了莫比乌斯反演后,感觉与以前做题完全不同了(飞升的感觉
居中的式子是大体思路
题目让我们求
$$\sum_{i=1}^n\sum_{j=1}^mgcd(i , j) == p$$
显然有一个结论$d | gcd(i,j) ⇔ d | i , d | j$
我们继续推
$f(i)表示i是不是质数,如果是的话,f(i) = 1,不是f(i) = 0$
$$\sum_{i=1}^n\sum_{i=1}^mf(gcd(i,j))$$
反演$$f(n) = \sum_{d|n}^ng(d)$$
就有$g(n) = \sum_{d|n}f(d)\mu (\dfrac{n}{d})$
$g(n) = \sum_{p\mid n}\mu\left(\frac np\right)(p为质数)$
$$\sum_{i=1}^n\sum_{i=1}^m\sum_{d|gcd(i,j)}g(d)$$
根据结论:
$$\sum_{i=1}^n\sum_{j=1}^m\sum_{d|i,d|j}g(d)$$
换个枚举点
$$\sum_{d=1}^{min(n,m)}g(d)\sum_{i=1}^n\sum_{j=1}^m{[d|i][d|j]}$$
$$\sum_{d=1}^{min(n,m)}g(d)[\dfrac{n}{d}][\dfrac{m}{d}]$$
推完了
用整除分块做
前面的式子可以用前缀和维护.

// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#define rep(i , x, p) for(register int i = x;i <= p;++ i)
#define sep(i , x, p) for(register int i = x;i >= p;-- i)
#define gc getchar()
#define pc putchar
#define ll long long
using std::max;
using std::swap;
const int maxN = 1e7 + 7;
const int N = 10000000;

inline int gi() {register int x = 0,f = 1;char c = gc;while(c < '0' || c > '9') {if(c == '-')f = -1;c = gc;}while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = gc;}return x * f;}
void print(ll x) {if(x < 0) pc('-') , x = -x;if(x >= 10) print(x / 10);pc(x % 10 + '0');}

inline int min(int a , int b) {return a > b ? b : a;}

int mu[maxN] , g[maxN], prime[maxN], num;
bool vis[maxN];

void init() {
    mu[1] = 1;
    rep(i , 2, N) {
        if(!vis[i]) {prime[++ num] = i;mu[i] = -1;}
        for(register int j = 1;j <= num && i * prime[j] <= N;++ j) {
            vis[i * prime[j]] = true;
            if(i % prime[j] == 0) break;
            mu[i * prime[j]] -= mu[i];
        }
    }
    rep(i , 1, num) {
        for(register int p = prime[i] , j = 1;j * p <= N;++ j) 
            g[j * p] += mu[j];
    }
    rep(j , 1, N) g[j] += g[j - 1];
}

inline ll work(int n , int m) {
    ll sum = 0;
    int tmp = min(n , m);
    for(register int l = 1 , r;l <= tmp;l = r + 1){
        r = min(n / (n / l) , m / (m / l));
        sum += (ll)(n / l) * (m / l) * (g[r] - g[l - 1]) ;
    }
    return sum;
}

int main() {
    init();
    int T = gi();
    while(T --) {
        int n = gi() , m = gi();
        print(work(n , m));pc('\n');
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/gzygzy/p/10121656.html