1. 程式人生 > >[luogu]P2522 [HAOI2011]Problem b[莫比烏斯反演]

[luogu]P2522 [HAOI2011]Problem b[莫比烏斯反演]

題目

T組詢問,每組詢問A,B,C,D,K共5個引數計算\sum_{A \leq x \leq B,C \leq y \leq D} [gcd(x,y)==k]

資料範圍:所有value \leq 50000

題解

F[x],f[x]分別為gcd(i,j)(1 \leq i \leq N,1 \leq j \leq M)x​​​​​​倍數和等於x的個數,有F[n]=\sum_{n|d}f[d]=\lfloor \frac{N}{n} \rfloor\lfloor \frac{M}{n} \rfloor

由莫比烏斯反演知:f[n]=\sum_{n|d}\mu (\frac{d}{n})F[d]

問題即求f^{'}[K],由於上界下界的問題,利用容斥分別計算即可:

以上界進行表示ans=solve(B,D)-solve(A-1,D)-solve(B,C-1)+solve(A-1,B-1)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

using LL = long long;
const int MAXN = 5e4 + 5;
int T, A, B, C, D, K;
bool noprime[MAXN];
int prime[MAXN], cnt_p, mu[MAXN];
void Euler_sieve(int top);
int pre[MAXN];
LL solve(int, int);

int main(){
  ios::sync_with_stdio(false);
  Euler_sieve(5e4);
  cin >> T;
  while(T--){
    cin >> A >> B >> C >> D >> K;
    LL ans = 0;
    ans += solve(B / K, D / K);
    ans -= solve(B / K, (C - 1) / K) + solve((A - 1) / K, D / K);
    ans += solve((A - 1) / K, (C - 1) / K);
    cout << ans << endl;
  }
  return 0;
}

LL solve(int n, int m){
  int l, r, top = min(n, m); LL res = 0;
  for(l = 1; l <= top; l = r + 1){
    r = min(n / (n / l), m / (m / l));
    res += 1LL * (pre[r] - pre[l - 1]) * (n / l) * (m / l);
  }
  return res;
}

void Euler_sieve(int top){
  int i, j;
  mu[1] = 1;
  for(i = 2; i <= top; i++){
    if(!noprime[i]) prime[++cnt_p] = i, mu[i] = -1;
    for(j = 1; j <= cnt_p && prime[j] * i <= top; j++){
      noprime[prime[j] * i] = true;
      if(i % prime[j] == 0) break;
      mu[prime[j] * i] = -mu[i];
    }
  }

  for(i = 1; i <= top; i++) pre[i] = pre[i - 1] + mu[i];
}