p1829 crash的数字表格
题目要我们求:
(displaystyle sum_{i=1}^nsum_{j=1}^m lcm(i,j)) 也就是 (displaystyle sum_{i=1}^nsum_{j=1}^m frac{ij}{gcd(i,j)})
然后我们熟练地接着向下化
(displaystyle sum_{k=1}^{min(n,m)}sum_{i=1}^nsum_{j=1}^m [gcd(i,j)=k]frac{ij}{k})
先去掉第一个(sigma),单独看后面,可以转化成
(displaystyle ksum_{i=1}^{lfloorfrac{n}{k} floor}sum_{j=1}^{lfloorfrac{m}{k} floor} [gcd(i,j)=1]ij)
不看前面的k,然后莫比乌斯反演
(displaystyle sum_{i=1}^{lfloorfrac{n}{k} floor}sum_{j=1}^{lfloorfrac{m}{k} floor} sum_{d|gcd(i,j)}mu(d) ij)
把莫比乌斯函数提到前面
(displaystyle sum_{d=1}^{min(lfloorfrac{n}{k} floor,lfloorfrac{m}{k} floor)}mu(d) sum_{i=1}^{lfloorfrac{n}{k} floor}sum_{j=1}^{lfloorfrac{m}{k} floor} [d|gcd(i,j)]ij)
我们故技重施,再来一次经典转化
(displaystyle sum_{d=1}^{min(lfloorfrac{n}{k} floor,lfloorfrac{m}{k} floor)}mu(d) d^2sum_{i=1}^{lfloorfrac{n}{kd} floor}sum_{j=1}^{lfloorfrac{m}{kd} floor} [1|gcd(i,j)]ij)
也就是
(displaystyle sum_{d=1}^{min(lfloorfrac{n}{k} floor,lfloorfrac{m}{k} floor)}mu(d) d^2sum_{i=1}^{lfloorfrac{n}{kd} floor}isum_{j=1}^{lfloorfrac{m}{kd} floor}j)
然后就化完了,把之前省略掉的加上
(displaystyle sum_{k=1}^{min(n,m)} ksum_{d=1}^{min(lfloorfrac{n}{k} floor,lfloorfrac{m}{k} floor)}mu(d) d^2sum_{i=1}^{lfloorfrac{n}{kd} floor}isum_{j=1}^{lfloorfrac{m}{kd} floor}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
",ans);
return 0;
}