bzoj2820 - YY的GCD (莫比乌斯函数,筛法,数论分块)

bzoj2820 - YY的GCD (莫比乌斯函数,筛法,数论分块)

题目

给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对(多组输入)

题解

前置题目:

\[\sum\limits_p{\sum\limits^n_{i=1}{\sum\limits^m_{j=1}{[\gcd(i,j)=p]}}} \]

\[\sum\limits_p{\sum\limits^{\frac{n}{p}}_{i=1}{\sum\limits^{\frac{m}{p}}_{j=1}{\sum\limits_{d|\gcd(i,j)}{\mu(d)}}}} \]

\[\sum\limits_p{\sum\limits_{d}{\mu(d)\sum\limits^{\frac{n}{p}}_{i=1}{[d|i]\sum\limits^{\frac{m}{p}}_{j=1}{[d|j]}}}} \]

\[\sum\limits_p{\sum\limits_{d}{\mu(d) \lfloor\frac{n}{pd}\rfloor\lfloor\frac{m}{pd}\rfloor}} \]

\(T=pd\)

\[\sum\limits^{min(n, m)}_{T=1}{\sum\limits_{p|T}{\mu(\frac{T}{p}) \lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor}} \]

\(f(T)=\sum\limits_{p|T}{\mu(\frac{T}{p})}\),它可以用埃式筛筛出来。然后求得\(f(T)\),接下来就是简单的数论分块了。细节详见代码。

#include <bits/stdc++.h>

#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N) 
typedef long long ll;

using namespace std;
/*-----------------------------------------------------------------*/

ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f

const int N = 1e7 + 10;
const double eps = 1e-5;

ll f[N];
int mu[N];
int cnt;
int prime[N];
bool isnp[N];
ll pre[N];

void init() {
    mu[1] = 1;
    f[1] = 0;
    for(int i = 2; i < N; i++) {
        if(!isnp[i]) {
            mu[i] = -1;
            prime[cnt++] = i;
        }
        for(int j = 0; j < cnt; j++) {
            int p = prime[j];
            if(i * p >= N) break;
            isnp[i * p] = 1;
            if(i % p == 0) {
                mu[i * p] = 0;
                break;
            }
            mu[i * p] = -mu[i];
        }
    }
    
    for(int i = 2; i < N; i++) { //埃式筛筛f(T)
        if(!isnp[i]) {
            f[i] = 1;
            for(int j = 2 * i; j < N; j += i) {
                f[j] += mu[j / i];
            }
        }
    }
    pre[0] = 0;
    for(int i = 1; i < N; i++) pre[i] += pre[i - 1] + f[i]; //前缀和
}


int main() {
    IOS;
    init();
    int t;
    cin >> t;
    while(t--) {
        int n, m;
        cin >> n >> m;
        ll ans = 0;
        int cur = 1;
        while(cur <= min(n, m)) { //数论分块
            int p1 = n / (n / cur);
            int p2 = m / (m / cur);
            int nt = min(min(n, m), min(p1, p2));
            ans += 1ll * (n / cur) * (m / cur) * 1ll * (pre[nt] - pre[cur - 1]);
            cur = nt + 1;
        }
        cout << ans << endl;
    }
}
posted @ 2020-11-30 20:09  limil  阅读(90)  评论(0编辑  收藏  举报