P1829 [国家集训队]Crash的数字表格 / JZPTAB
题意
给出 n,m 求解:
i=1∑nj=1∑mlcm(i,j)
1⩽n,m⩽107
思路
对原式进行数论变换:
i=1∑nj=1∑mlcm(i,j)=i=1∑nj=1∑mgcd(i,j)i⋅j提取公因式d=1∑min(n,m)i=1∑nj=1∑mdi⋅j⋅[gcd(i,j)=d]i=i/d,j=j/dd=1∑min(n,m)i=1∑⌊dn⌋j=1∑⌊dm⌋did⋅jd⋅[gcd(id,jd)=d]=d=1∑min(n,m)i=1∑⌊dn⌋j=1∑⌊dm⌋i⋅j⋅d⋅[gcd(i,j)=1]=d=1∑min(n,m)di=1∑⌊dn⌋j=1∑⌊dm⌋i⋅j⋅ε(gcd(i,j))=d=1∑min(n,m)di=1∑⌊dn⌋j=1∑⌊dm⌋i⋅j⋅(μ∗1)(gcd(i,j))=d=1∑min(n,m)di=1∑⌊dn⌋j=1∑⌊dm⌋i⋅j⋅t∣gcd(i,j)∑μ(t)=d=1∑min(n,m)dt=1∑min(⌊dn⌋,⌊dm⌋)i=1∑⌊dn⌋[t∣i]j=1∑⌊dm⌋[t∣j]⋅i⋅j⋅μ(t)i=i/t,j=j/td=1∑min(n,m)dt=1∑min(⌊dn⌋,⌊dm⌋)i=1∑⌊dtn⌋j=1∑⌊dtm⌋it⋅jt⋅μ(t)=d=1∑min(n,m)dt=1∑min(⌊dn⌋,⌊dm⌋)t2μ(t)i=1∑⌊dtn⌋ij=1∑⌊dtm⌋j=d=1∑min(n,m)dt=1∑min(⌊dn⌋,⌊dm⌋)t2μ(t)⋅2(1+⌊dtn⌋)(⌊dtn⌋)⋅2(1+⌊dtm⌋)(⌊dtm⌋)
其中,数论函数 ε,μ,1 见 积性函数-例子
最终的式子可以使用二维 数论分块 解决,用线性筛筛出 μ 函数,顺便维护 t2μ(t) 的前缀和。
总复杂度 O(N⋅N)=O(N)。
点击显/隐代码
#include <bits/stdc++.h>
#define db double
#define ll long long
#define int ll
#define vi vector<int>
#define vii vector<vi >
#define pii pair<int, int>
#define vp vector<pii >
#define vip vector<vp >
#define mkp make_pair
#define pb push_back
#define Case(x) cout << "Case #" << x << ": "
using namespace std;
const int INF = 0x3f3f3f3f;
const int P = 20101009;
const int N = 1e7 + 10;
int mu[N], sum[N];
bool vis[N];
vi prim;
void Euler(int n) {
mu[1] = sum[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) {
prim.pb(i);
mu[i] = -1;
}
for (auto j : prim) {
int t = i * j;
if (t > n) break;
vis[t] = 1;
if (i % j == 0) {
mu[t] = 0;
break;
}
mu[t] = -mu[i];
}
sum[i] = (sum[i-1] + i * i % P * mu[i] + P) % P;
}
}
signed main(){
#ifdef _DEBUG
// FILE *file = freopen("out", "w", stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);
Euler(1e7);
int n, m, ans = 0;
cin >> n >> m;
int mn1 = min(n, m);
for (int l = 1, r; l <= mn1; l = r + 1) {
r = min(n/(n/l), m/(m/l));
int mn2 = min(n/l, m/l), tmp = 0;
for (int u = 1, v, s = n/l, t = m/l; u <= mn2; u = v + 1) {
v = min(s/(s/u), t/(t/u));
tmp = (tmp + (sum[v] - sum[u-1] + P) % P * ((1+s/u)*(s/u)/2%P) % P * ((1+t/u)*(t/u)/2%P) % P) % P;
}
ans = (ans + (l + r) * (r - l + 1) / 2 % P * tmp % P) % P;
}
cout << ans << '\n';
return 0;
}