1. 程式人生 > >[BZOJ2301][HAOI2011]Problem B

[BZOJ2301][HAOI2011]Problem B

oid 取值 lse 由於 printf 快速 ons code fill

題目大意

對於給出的 \(n\) 次詢問,每次給定 \(a,b,c,d,k\) 五個數,詢問 \(\displaystyle \sum_{x=a}^b\sum_{y=c}^d\big[(x,y)=k\big]\) 的值。數據保證 \(n\leqslant 50000,1\leqslant a\leqslant b\leqslant 50000,1\leqslant c\leqslant d\leqslant 50000\)

解析

首先利用容斥原理將一個詢問拆分成四個,每次求 \(\displaystyle \sum_{x=1}^n\sum_{y=1}^m\big[(x,y)=k\big]\) 的值。考慮莫比烏斯反演

,設 \(F(k)\)\(k|(x,y)\) 的對數,\(f(k)\)\((x,y)=k\) 的個數,則有以下關系:
\[F(k)=\sum_{x=1}^{\big\lfloor\frac{n}{k}\big\rfloor}f(k\cdot x)\]
只需快速求出 \(F(k)\)。若 \(k|x,k|y\),則 \(x=k\cdot t_1,y=k\cdot t_2\),只需統計有多少對 \((t_1,t_2)\)。得:
\[\displaystyle F(k)=\Big\lfloor\frac{n}{k}\Big\rfloor\Big\lfloor\frac{m}{k}\Big\rfloor\]

由莫比烏斯反演可求得 \(f(k)\)
\[f(k)=\sum_{x=1}^{\big\lfloor\frac{n}{k}\big\rfloor}\bigg(\mu(x)\cdot F(k\cdot x)\bigg)=\sum_{x=1}^{\big\lfloor\frac{n}{k}\big\rfloor}\bigg(\mu(x)\Big\lfloor\frac{n}{kx}\Big\rfloor\Big\lfloor\frac{m}{kx}\Big\rfloor\bigg)\]
暴力求 \(f(k)\)\(O(n)\),註意到 \(\displaystyle \Big\lfloor\frac{n}{d}\Big\rfloor\)
有至多 \(O(\sqrt{n})\) 個取值,所以可以考慮使用數論分塊求解

證明:


  • \(1\leqslant d < \sqrt{n}\) 時,由於 \(d\) 只有 \(\sqrt{n}\) 個,所以 \(\displaystyle \Big\lfloor\frac{n}{d}\Big\rfloor\) 也至多有 \(\sqrt{n}\) 個取值。

  • \(\sqrt{n}\leqslant d\leqslant n\) 時,由於 \(\displaystyle \Big\lfloor\frac{n}{d}\Big\rfloor\) 只有 \(\sqrt{n}\) 個,所以 \(\displaystyle \Big\lfloor\frac{n}{d}\Big\rfloor\) 也只有至多 \(\sqrt{n}\) 個取值。

  • \(\displaystyle \Big\lfloor\frac{n}{d}\Big\rfloor\) 至多有 \(O(\sqrt{n})\) 個取值。

同理 \(\displaystyle \Big\lfloor\frac{m}{d}\Big\rfloor\) 也只有至多 \(O(\sqrt{m})\) 個取值,所以 \(\displaystyle \Big\lfloor\frac{n}{kx}\Big\rfloor\)\(\displaystyle \Big\lfloor\frac{m}{kx}\Big\rfloor\) 都不變的段數有 \(O(\sqrt{n}+\sqrt{m})\) 段。對於相等的段,求出 \(\mu\) 函數的前綴和,即可批量計算這一個段的答案。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 50010;
int T, tot, prime[maxn], mu[maxn], sum[maxn];

void sieve() {
    fill(prime, prime + maxn, 1);
    mu[1] = 1, tot = 0;
    for (int i = 2; i < maxn; i++) {
        if (prime[i]) {
            prime[++tot] = i, mu[i] = -1;
        }
        for (int j = 1; j <= tot && i * prime[j] < maxn; j++) {
            prime[i * prime[j]] = 0;
            if (i % prime[j] == 0) {
                mu[i * prime[j]] = 0;
                break;
            } else {
                mu[i * prime[j]] = -mu[i];
            }
        }
    }
    for (int i = 1; i < maxn; i++) {
        sum[i] = sum[i - 1] + mu[i];
    }
}

int calc(int n, int m, int k) {
    if (n > m) swap(n, m);
    int ans = 0;
    n /= k, m /= k;
    for (int i = 1, nxt = 1; i <= n; i = nxt + 1) {
        nxt = min(n / (n / i), m / (m / i));
        ans += (sum[nxt] - sum[i - 1]) * (n / i) * (m / i);
    }
    return ans;
}

int main() {
    sieve();
    scanf("%d", &T);
    while (T--) {
        int a, b, c, d, k;
        scanf("%d %d %d %d %d", &a, &b, &c, &d, &k);
        printf("%d\n", calc(b, d, k) - calc(b, c - 1, k) - calc(a - 1, d, k) + calc(a - 1, c - 1, k));
    }
    return 0;
}

[BZOJ2301][HAOI2011]Problem B