1. 程式人生 > >[HAOI 2011]Problem b

[HAOI 2011]Problem b

$1 over haoi 2011 nbsp mes 化簡 lock std pri

Description

對於給出的n個詢問,每次求有多少個數對(x,y),滿足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函數為x和y的最大公約數。

Input

第一行一個整數n,接下來n行每行五個整數,分別表示a、b、c、d、k

Output

共n行,每行一個整數表示滿足要求的數對(x,y)的個數

Sample Input

2
2 5 1 5 1
1 5 1 5 2

Sample Output

14
3

HINT

100%的數據滿足:1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000

題解

記 $f(x, y)$ 為滿足 $gcd(i,j)=k$ 的數對 $(i,j)~~(i\in[1,x],j\in[1,y])$ 的個數。

由容斥原理,答案就是 $ans=f(b,d)-f((a-1),d)-f(b,(c-1))+f((a-1),(c-1))$ 。

我們現在考慮如何求 $f(x,y)$ 。

我們按照以往的套路,將 $k$ 提出,顯然 $$f(a,b)=\sum_{i=1}^{\left\lfloor\frac{a}{k} \right\rfloor}\sum_{j=1}^{\left\lfloor\frac{b}{k} \right\rfloor}[gcd(i,j)=1]$$

由公式 $\sum_{d\mid n} \mu(d)=[n=1]$ ,我們得到

$$\Rightarrow \sum_{i=1}^{\left\lfloor\frac{a}{k} \right\rfloor}\sum_{j=1}^{\left\lfloor\frac{b}{k} \right\rfloor}\sum_{d\mid gcd(i,j)}\mu(d)$$

我們將 $\mu$ 提前

\begin{aligned} &\Rightarrow\sum_{d=1}^{min\left\{\left\lfloor\frac{a}{k} \right\rfloor,\left\lfloor\frac{b}{k} \right\rfloor\right\}}\mu(d)\sum_{i=1,d\mid i}^{\left\lfloor\frac{a}{k} \right\rfloor}\sum_{j=1,d\mid j}^{\left\lfloor\frac{b}{k}\right\rfloor}1\\&\Rightarrow\sum_{d=1}^{min\left\{\left\lfloor\frac{a}{k} \right\rfloor,\left\lfloor\frac{b}{k} \right\rfloor\right\}}\mu(d)\cdot\left\lfloor\frac{\left\lfloor\frac{a}{k}\right\rfloor}{d}\right\rfloor\cdot\left\lfloor\frac{\left\lfloor\frac{b}{k}\right\rfloor}{d}\right\rfloor\end{aligned}

得到這個式子還有第二個方Fa♂,這裏也提一下。

令 $F(d)$ 為 $d\mid gcd(i,j)$ 的數對 $(i,j)$ 個數, $f(d)$ 為 $d=gcd(i,j)$ 的數對 $(i,j)$ 個數。

由題 $$F(k)=\sum_{d=1}^{min\left\{\left\lfloor\frac{a}{k}\right\rfloor,\left\lfloor\frac{b}{k} \right\rfloor\right\}}f(kd)$$

由莫比烏斯反演定理 $F(n)=\sum_{n\mid d} f(d)\Rightarrow f(n)=\sum_{n\mid d} \mu(\frac{d}{n})F(d)$

\begin{aligned}\Rightarrow f(k)&=\sum_{d=1}^{min\left\{\left\lfloor\frac{a}{k}\right\rfloor,\left\lfloor\frac{b}{k}\right\rfloor\right\}}\mu(d)F(kd)\\&=\sum_{d=1}^{min\left\{\left\lfloor\frac{a}{k} \right\rfloor,\left\lfloor\frac{b}{k}\right\rfloor\right\}}\mu(d)\cdot\left\lfloor\frac{a}{kd}\right\rfloor\cdot\left\lfloor\frac{b}{kd}\right\rfloor\end{aligned}

那麽我們現在只需要 $O(n)$ 的枚舉 $d$ 就可以了,但對於多組詢問,這個復雜度還是吃不消的。

我們考慮在計算的時候 $\left\lfloor\frac{\left\lfloor\frac{n}{k}\right\rfloor}{d}\right\rfloor$ 是一個分段的,有較長的一段區間內的數是相等的。可以證明這段區間的長度是 $\sqrt{n}$ 的。

證明:

  1.當 $1\leq d < \sqrt n$ 時, $d$ 就只有 $\sqrt n$ 個, $\left\lfloor{n \over d}\right\rfloor$ 最多有 $\sqrt n$ 個。

  2.當 $\sqrt n \leq d \leq n$ 時,由於 $\left\lfloor{n \over d}\right\rfloor$ 小於 $\sqrt n$ ,所以 $\left\lfloor{n \over d}\right\rfloor$ 最多有 $\sqrt n$ 個。

證畢。

顯然對於一段區間內 $\left\lfloor\frac{\left\lfloor\frac{a}{k}\right\rfloor}{d}\right\rfloor$$\left\lfloor\frac{\left\lfloor\frac{b}{k}\right\rfloor}{d}\right\rfloor$ 相同的數我們可以直接處理 $\mu$ 的前綴,直接求即可。而相同值的區間的右端點為 $\left\lfloor \frac{n}{\left\lfloor\frac{n}{d} \right\rfloor} \right\rfloor$ 。

證明:

對於 $n$ 來說,找到最大的 $j$ 滿足 $$\left\lfloor\frac{n}{i}\right\rfloor \leq\left\lfloor\frac{n}{j}\right\rfloor$$

化簡一下得到 $$j\leq \left\lfloor\frac{n}{\left\lfloor\frac{n}{i}\right\rfloor}\right\rfloor$$

那麽在計算時在分別關於 $a, b$ 的表達式中取一個較小值即可。時間復雜度 $O(n\sqrt n)$ 。

 1 //It is made by Awson on 2018.1.19
 2 #include <set>
 3 #include <map>
 4 #include <cmath>
 5 #include <ctime>
 6 #include <queue>
 7 #include <stack>
 8 #include <cstdio>
 9 #include <string>
10 #include <vector>
11 #include <cstdlib>
12 #include <cstring>
13 #include <iostream>
14 #include <algorithm>
15 #define LL long long
16 #define Abs(a) ((a) < 0 ? (-(a)) : (a))
17 #define Max(a, b) ((a) > (b) ? (a) : (b))
18 #define Min(a, b) ((a) < (b) ? (a) : (b))
19 #define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b))
20 #define writeln(x) (write(x), putchar(‘\n‘))
21 using namespace std;
22 const int N = 50000;
23 const int INF = ~0u>>1;
24 void read(int &x) {
25     char ch; bool flag = 0;
26     for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == -)) || 1); ch = getchar());
27     for (x = 0; isdigit(ch); x = (x<<1)+(x<<3)+ch-48, ch = getchar());
28     x *= 1-2*flag;
29 }
30 void write(LL x) {
31     if (x > 9) write(x/10);
32     putchar(x%10+48);
33 }
34 
35 LL mu[N+5]; int a, b, c, d, k;
36 void get_mu() {
37     int isprime[N+5], prime[N+5], tot = 0;
38     memset(isprime, 1, sizeof(isprime)); isprime[1] = 0, mu[1] = 1;
39     for (int i = 2; i <= N; i++) {
40     if (isprime[i]) mu[i] = -1, prime[++tot] = i;
41     for (int j = 1; j <= tot && i*prime[j] <= N; j++)
42         if (!(i%prime[j])) {isprime[i*prime[j]] = 0, mu[i*prime[j]] = 0; break; }
43         else isprime[i*prime[j]] = 0, mu[i*prime[j]] = -mu[i];
44     mu[i] += mu[i-1];
45     }
46 }
47 LL cal(int x, int y) {
48     if (x > y) Swap(x, y); LL ans = 0;
49     for (int i = 1, last; i <= x; i = last+1) {
50     last = Min(x/(x/i), y/(y/i));
51     ans += (LL)(mu[last]-mu[i-1])*(x/i)*(y/i);
52     }
53     return ans;
54 }
55 void work() {
56     read(a), read(b), read(c), read(d), read(k);
57     writeln(cal(b/k, d/k)-cal(b/k, (c-1)/k)-cal((a-1)/k, d/k)+cal((a-1)/k, (c-1)/k));
58 }
59 int main() {
60     int t; read(t); get_mu();
61     while (t--) work();
62     return 0;
63 }

[HAOI 2011]Problem b