题解【bzoj2301 [HAOI2011]Problem b】

Description

求有多少个数对 \((x,y)\) ,满足$ a \leq x \leq b$ ,\(c \leq y \leq d\) ,且 \(\gcd(x,y) = k\)\(\gcd(x,y)\)函数为 \(x\)\(y\) 的最大公约数。多组询问。\(a,b,c,d,k,T \leq 50000\)

Solution

莫比乌斯反演的经典题目QAQ

首相将问题转化成前缀上的问题。即需要求出 有多少个数对 \((x,y)\) ,满足$ 1 \leq x \leq a$ ,\(1 \leq y \leq b\) ,且 \(\gcd(x,y) = k\)。如果能够快速算出来这个,容斥一下就可以求出最后答案。

考虑这个怎么求,开始推式子。这个东西显然就是

\[\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[\gcd(i,j)=k]\]

\(k\) 提出来可得

\[\sum\limits_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{k}\rfloor}[\gcd(i, j)=1]\]

然后把后面这个 \([\gcd(i,j)=1]\) 反演掉,得

\[\sum\limits_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{k}\rfloor}\sum\limits_{d|\gcd(i,j)}\mu(d)\]

\(d\) 搞到前面来,得到

\[\sum\limits_{d=1}^{\lfloor \frac{n}{k} \rfloor} \mu(d)\lfloor \frac{n}{kd} \rfloor\lfloor \frac{m}{kd} \rfloor\]

好了,这个玩意可以预处理出 \(\mu\) 得前缀和然后分块完事。

Code

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 50000; 
int k, cnt, p[N + 50], mu[N + 50], flag[N + 50], sum[N + 50]; 
inline void prework() {
  flag[1] = mu[1] = 1;
  for(int i = 2; i <= N; i++) {
    if(!flag[i]) {
      p[++cnt] = i; mu[i] = -1; 
    } for(int j = 1; j <= cnt && i * p[j] <= N; j++) {
      flag[i * p[j]] = 1;
      if(i % p[j] == 0) {
        mu[i * p[j]] = 0; break; 
      } mu[i * p[j]] = mu[i] * -1; 
    }
  } for(int i = 1; i <= N; i++) sum[i] = sum[i - 1] + mu[i]; 
}
inline ll calc(int n, int m) {
  if(n > m) swap(n, m); ll ret = 0; 
  for(int l = 1, r; l <= n / k; l = r + 1) {
    r = min(n / (n / l), m / (m / l)); 
    ret += 1ll * (n / (l * k)) * (m / (l * k)) * (sum[r] - sum[l - 1]); 
  } return ret; 
}
int main() {
  int T; prework(); 
  scanf("%d", &T); 
  while(T--) {
    int a, b, c, d; 
    scanf("%d %d %d %d %d", &a, &b, &c, &d, &k);
    printf("%lld\n", calc(a - 1, c - 1) - calc(b, c - 1) - calc(d, a - 1) + calc(b, d)); 
  }
  return 0; 
}
posted @ 2018-12-16 19:07  AcFunction  阅读(...)  评论(...编辑  收藏