[HAOI2011]Problem b
化成数学代数式就是这样:
$$\sum_{x=a}^b\sum_{y=c}^d[\gcd(x,y)=k]$$
根据差分的思想,可以将该式变成:
$$\sum_{x=1}^b\sum_{y=1}^d[\gcd(x,y)=k]-\sum_{x=1}^{a-1}\sum_{y=1}^d[\gcd(x,y)=k]-\sum_{x=1}^b\sum_{y=1}^{c-1}[\gcd(x,y)=k]+\sum_{x=1}^{a-1}\sum_{y=1}^{b-1}[\gcd(x,y)=k]$$
只要求出:
$$\sum_{i=1}^N\sum_{j=1}^M[\gcd(i,j)=k]$$
最后的答案就能求解。
我们来推导这个式子:
$$\begin{array}{ll} &\sum\limits_{i=1}^N\sum\limits_{j=1}^M[\gcd(i,j)=k]\\=&\sum\limits_{i=1}^{\lfloor\frac Nk\rfloor}\sum\limits_{j=1}^{\lfloor\frac Mk\rfloor}[\gcd(i,j)=1]\\ =&\sum\limits_{i=1}^{\lfloor\frac Nk\rfloor}\sum\limits_{j=1}^{\lfloor\frac Mk\rfloor}\epsilon(\gcd(i,j)) & \text{根据单位函数ϵ的定义}\\=&\sum\limits_{i=1}^{\lfloor\frac Nk\rfloor}\sum\limits_{j=1}^{\lfloor\frac Mk\rfloor}\sum\limits_{d|gcd(i,j)}\mu(d)&\text{注意这一步的变换是根据} \epsilon=\mu*1 \text{得来的}\\=&\sum\limits_{i=1}^{\lfloor\frac Nk\rfloor}\sum\limits_{j=1}^{\lfloor\frac Mk\rfloor}\sum\limits_{d|i,d|j}\mu(d)\\=&\sum\limits_{d=1}^{\min(N,M)}\mu(d)\sum\limits_{d|i,i\leq \lfloor\frac Nk\rfloor}\sum\limits_{d|j,j\leq \lfloor\frac Mk\rfloor}&\text{调换求和顺序} \\=&\sum\limits_{d=1}^{\min(N,M)}\mu(d) \lfloor \frac{\lfloor \frac Nk \rfloor}d \rfloor \lfloor \frac{\lfloor \frac Mk \rfloor}d \rfloor \end{array}$$
现在可以用$O(\min(N,M))$的复杂度解决,但还不够。我们要用一种科技:整除分块。
有一个性质:一个数$N$整除任意数,商最多有$2\sqrt{N}$种不同的数;
证明:设除数$d$,当$d\leq \sqrt{N}$,最多有$\sqrt{N}$种除数;当$d\leq \sqrt{N}$,$0\leq\left\lfloor \dfrac Nd \right\rfloor\leq \sqrt{N}$最多只有$\sqrt{N}$种,综上最多有$2\sqrt{N}$种。
再一个性质:一个数$N$与另一个正整数$l$整除为$d$,在$d$不变的情况下,$l$的最大值$l'$为$\left\lfloor \dfrac N{\lfloor \frac Nl \rfloor} \right\rfloor$;
证明:回想下小学的东西:被除数一定,商越小,除数越大。因为是整除,所以商是整数,如果不是整除,商$d'$一定满足$d\leq d' <d+1$,所以除数最大时最小的商$d'=d=\left\lfloor\dfrac Nl\right\rfloor$。除数的最大值$l'$就是$\left\lfloor \dfrac N{\lfloor \frac Nl \rfloor} \right\rfloor$了(加下取整是因为$l'$是整数)。
根据上面两个性质,算法就应运而生了。
代码实现(这里用$r$代替了上面的$l'$):
for (int l = 1, r; l <= N; l = r+1) { r = N / (N / l); // do something }
复杂度为$O(\sqrt{N})$。
然后我们可以通过分块求出当商相等的$d$的区间再乘上$\mu$的前缀差分即可。
关于$\mu$的求解,因为是积性函数,可以用线性筛筛出,复杂度$O(\text{size})$,然后直接前缀处理,最后分块统计即可。
整体复杂度:$O(T\sqrt N)$,这里$T$是数据组数,$N$为$a,b,c,d$的范围。详见底下的代码。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define re register 6 #define rep(i, a, b) for (re int i = a; i <= b; ++i) 7 #define repd(i, a, b) for (re int i = a; i >= b; --i) 8 #define srep(i, a, b, c) for (re int i = a; i <= b; c) 9 #define maxx(a, b) a = max(a, b); 10 #define minn(a, b) a = min(a, b); 11 #define LL long long 12 #define INF (1 << 30) 13 14 inline int read() { 15 int w = 0, f = 1; char c = getchar(); 16 while (!isdigit(c)) f = c == '-' ? -1 : f, c = getchar(); 17 while (isdigit(c)) w = (w << 3) + (w << 1) + (c ^ '0'), c = getchar(); 18 return w * f; 19 } 20 21 const int maxn = 5e4 + 5; 22 23 int mu[maxn], vis[maxn], p[maxn], prime_tot; 24 25 void get_mu() { 26 prime_tot = 0; 27 memset(vis, 0, sizeof(vis)); 28 mu[1] = 1; int size = 50000; 29 rep(i, 2, size) { 30 if (!vis[i]) { 31 p[prime_tot++] = i; 32 mu[i] = -1; 33 } 34 rep(j, 0, prime_tot-1) { 35 if (i * p[j] > size) break; 36 vis[i * p[j]] = 1; 37 if (!(i % p[j])) { 38 mu[i * p[j]] = 0; 39 break; 40 } 41 mu[i * p[j]] = -mu[i]; 42 } 43 } 44 rep(i, 2, size) mu[i] += mu[i-1]; 45 } 46 47 LL calc(int m, int n, int k) { 48 m /= k, n /= k; 49 int loop = min(m, n); 50 LL ans = 0; 51 for (int l = 1, r; l <= loop; l = r+1) { 52 r = min(n / (n / l), m / (m / l)); 53 ans += 1ll * (mu[r]-mu[l-1]) * (1ll * (n/l)*(m/l)); 54 } 55 return ans; 56 } 57 58 int a, b, c, d, e, n; 59 60 int main() { 61 n = read(); 62 get_mu(); 63 while (n--) { 64 a = read(), b = read(), c = read(), d = read(), e = read(); 65 printf("%lld\n", calc(b, d, e) - calc(a-1, d, e) - calc(b, c-1, e) + calc(a-1, c-1, e)); 66 } 67 return 0; 68 }