[luogu1829][bzoj2154][国家集训队]Crash的数字表格 / JZPTAB

传送门:洛谷bzoj

题目描述

今天的数学课上,Crash小朋友学习了最小公倍数(Least Common Multiple)。对于两个正整数a和b,LCM(a, b)表示能同时整除a和b的最小正整数。例如,LCM(6, 8) = 24。
回到家后,Crash还在想着课上学的东西,为了研究最小公倍数,他画了一张NM的表格。每个格子里写了一个数字,其中第i行第j列的那个格子里写着数为LCM(i, j)。一个45的表格如下:

1    2    3    4    5
2    2    6    4    10
3    6    3    12    15
4    4    12    4    20

看着这个表格,Crash想到了很多可以思考的问题。不过他最想解决的问题却是一个十分简单的问题:这个表格中所有数的和是多少。当N和M很大时,Crash就束手无策了,因此他找到了聪明的你用程序帮他解决这个问题。由于最终结果可能会很大,Crash只想知道表格里所有数的和mod20101009的值。

题解

蒟蒻chh来推导一下:
答案要求:
\[\sum^{n}_{i=1}\sum^{m}_{j=1}lcm(i,j)\]
根据小学知识,最小公倍数\(\times\)最大公约数=两个数的乘积。
那么答案就变成了:
\[\sum^{n}_{i=1}\sum^{m}_{j=1}\frac{i\times j}{gcd(i,j)}\]
整除分块枚举最大公约数\(d\),商互质
\[\sum^{n}{i=1} \sum{m}{j=1} \sum_{d|i,d|j,gcd(\frac{i}{d},\frac{j}{d})=1} \frac{i\times j}{d}\]
将这个gcd算式转换:
\[\sum^{n}_{d=1} \times \sum^{\lfloor\frac{n}{d}\rfloor}_{i=1} \sum^{\lfloor\frac{m}{d}\rfloor}_{j=1}[gcd(i,j)=1] \times i \times j\]
在将原来的式子进行简化
\[calc(n,m) = \sum^{n}_{i=1} \sum^{m}_{j=1}[gcd(i,j) = 1] \times i \times j\]
那么我们如果我们能算出\(calc\),那么就可以用数论分块求解。
枚举约数
\[\sum^{n}_{d=1} \sum^{n}_{d|i} \sum^{m}_{d|j} \mu (d) \times i \times j\]
\(i=i' \times d, j = j' \times d\),代入原式:
\[\sum^{n}_{d=1} \mu(d) \times d ^ 2 \times \sum^{\lfloor \frac{n}{d}\rfloor}_{i=1} \sum^{\lfloor \frac{m}{d}\rfloor}_{j=1} \times i \times j\]
莫比乌斯函数前缀和预处理前一部分答案,后一部分答案我们假设\(calc2(n,m)\)
那么以上答案又可以用一个数论分块求解。
\[calc2(n,m)=\sum^{n}_{i=1} \sum^{m}_{j=1} \times i \times j = \frac{n \times (n + 1)}{2} \times \frac{m\times (m+1)}{2}\]
以上可以\(O(1)\)计算。
那么我们把我们所有简化的式子代回去就可以了。
附赠大佬证明:

ac代码

#include <bits/stdc++.h>
#define ll long long
#define ms(a, b) memset(a, b, sizeof(a))
#define inf 0x3f3f3f3f
#define mod 20101009
#define N 10000005
using namespace std;
template <typename T>
inline void read(T &x) {
    x = 0; T fl = 1;
    char ch = 0;
    while (ch < '0' || ch > '9') {
        if (ch == '-') fl = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    x *= fl;
}
int n, m, prime_tot;
bool vis[N];
int prime[N], mu[N], sum[N];
void get_mu(int MAXN) {
    mu[1] = 1; 
    prime_tot = 0;
    for (int i = 2; i <= MAXN; i ++) {
        if (!vis[i]) {
            prime[++ prime_tot] = i;
            mu[i] = -1;
        }
        for (int j = 1; j <= prime_tot && prime[j] * i <= MAXN; j ++) {
            vis[prime[j] * i] = 1;
            if (i % prime[j] == 0) break;
            else mu[prime[j] * i] = -mu[i]; 
        }
    }
    for (int i = 1; i <= MAXN; i ++) {
        sum[i] = (sum[i - 1] + 1ll * i * i % mod * (mu[i] + mod)) % mod;
    }
}
int calc2(int x, int y) {
    return (1ll * x * (x + 1) / 2 % mod) * (1ll * y * (y + 1) / 2 % mod) % mod;
}
int calc(int x, int y) {
    int res = 0;
    for (int l = 1, r; l <= min(x, y); l = r + 1) {
        r = min(x / (x / l), y / (y / l));
        res = (res + 1ll * (sum[r] - sum[l - 1] + mod) * calc2(x / l, y / l) % mod) % mod;
    }
    return res; 
}
int main() {
    read(n); read(m);
    get_mu(min(n, m) + 1);
    int ans = 0;
    for (int l = 1, r; l <= min(n, m); l = r + 1) {
        r = min(n / (n / l), m / (m / l));
        ans = (ans + 1ll * (r - l + 1) * (r + l) / 2 % mod * calc(n / l, m / l) % mod) % mod;
    }
    printf("%d\n", ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/chhokmah/p/10548994.html