【题解】crash的数字表格
p1829 crash的数字表格
题目要我们求:
\(\displaystyle \sum_{i=1}^n\sum_{j=1}^m lcm(i,j)\) 也就是 \(\displaystyle \sum_{i=1}^n\sum_{j=1}^m \frac{ij}{gcd(i,j)}\)
然后我们熟练地接着向下化
\(\displaystyle \sum_{k=1}^{min(n,m)}\sum_{i=1}^n\sum_{j=1}^m [gcd(i,j)=k]\frac{ij}{k}\)
先去掉第一个\(sigma\),单独看后面,可以转化成
\(\displaystyle k\sum_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{k}\rfloor} [gcd(i,j)=1]ij\)
不看前面的k,然后莫比乌斯反演
\(\displaystyle \sum_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{k}\rfloor} \sum_{d|gcd(i,j)}\mu(d) ij\)
把莫比乌斯函数提到前面
\(\displaystyle \sum_{d=1}^{min(\lfloor\frac{n}{k}\rfloor,\lfloor\frac{m}{k}\rfloor)}\mu(d) \sum_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{k}\rfloor} [d|gcd(i,j)]ij\)
我们故技重施,再来一次经典转化
\(\displaystyle \sum_{d=1}^{min(\lfloor\frac{n}{k}\rfloor,\lfloor\frac{m}{k}\rfloor)}\mu(d) d^2\sum_{i=1}^{\lfloor\frac{n}{kd}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{kd}\rfloor} [1|gcd(i,j)]ij\)
也就是
\(\displaystyle \sum_{d=1}^{min(\lfloor\frac{n}{k}\rfloor,\lfloor\frac{m}{k}\rfloor)}\mu(d) d^2\sum_{i=1}^{\lfloor\frac{n}{kd}\rfloor}i\sum_{j=1}^{\lfloor\frac{m}{kd}\rfloor}j\)
然后就化完了,把之前省略掉的加上
\(\displaystyle \sum_{k=1}^{min(n,m)} k\sum_{d=1}^{min(\lfloor\frac{n}{k}\rfloor,\lfloor\frac{m}{k}\rfloor)}\mu(d) d^2\sum_{i=1}^{\lfloor\frac{n}{kd}\rfloor}i\sum_{j=1}^{\lfloor\frac{m}{kd}\rfloor}j\)
注意到后面两个\(\sum\)可以预处理,中间的莫比乌斯函数也可以预处理,然后数论分块求就好了
贴个代码吧
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define maxn 10000007
#define mod 20101009
long long n,m,sum[maxn],s[maxn];
int mu[maxn],prime[maxn],tot;
bool vis[maxn];
void getmu() {
int k = std::max(n,m);
mu[1] = 1;
for (int i = 2;i <=k;i++) {
if (!vis[i]) prime[++tot] = i,mu[i] = -1;
for (int j = 1;j <= tot && (long long)i * prime[j] <= n;j++) {
vis[i * prime[j]] = 1;
if (i % prime[j] == 0) {
mu[i * prime[j]] = 0;
break;
}
mu[i * prime[j]] = -mu[i];
}
}
for (int i = 1;i <= k;i++) sum[i] = (sum[i - 1] + (long long)mu[i] * (long long) i % mod * i % mod) % mod;
for (int i = 1;i <= k;i++) s[i] = (s[i - 1] + i) % mod;
}
int main() {
scanf("%lld%lld",&n,&m);
int u = std::min(n,m);
long long ans = 0;
getmu();
for (int k = 1;k <= u;k++) {
long long su = 0;
int p = 1;
while (p <= std::min(n / k,m / k)) {
int r = std::min((n / k) / (n / k / p),(m / k) / (m / k / p));
(su += (long long)(sum[r] - sum[p - 1] + mod) % mod * (long long)s[n / k / p] % mod * (long long)s[m / k / p] % mod) %= mod;
p = r + 1;
}
(ans += su * k % mod) %= mod;
}
printf("%lld\n",ans);
return 0;
}