题解:[国家集训队] Crash的数字表格 / JZPTAB
莫比乌斯反演
对于这种题目,显然是一个数学题,因此可以转换。
不妨钦定 \(n\leq m\),否则交换 \(n,m\)。
记答案为 \(\textit{ans}\):
\[\begin{aligned}
\textit{ans}&=\sum_{i=1}^n\sum_{j=1}^m\operatorname{lcm}(i,j)\\
&=\sum_{i=1}^n\sum_{j=1}^m\dfrac{ij}{\gcd(i,j)}\\
&=\sum_{d=1}^n\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=d]\frac{ij}{d}\\
\end{aligned}
\]
将枚举的 \(i,j\) 换为 \(i'd,j'd\),则 \(i'\perp j'\):
\[\textit{ans}=\sum_{d=1}^nd\sum_{i=1}^{\large\left\lfloor\frac nd\right\rfloor}\sum_{j=1}^{\large\left\lfloor\frac md\right\rfloor}[i\perp j]ij
\]
考虑到:
\[[i\perp j]=[\gcd(i,j)=1]=\sum_{d\mid\gcd(i,j)}\mu(d)=\sum_{d\mid i\land d\mid j}\mu(d)
\]
因此:
\[\textit{ans}=\sum_{d=1}^nd\sum_{i=1}^{\large\left\lfloor\frac nd\right\rfloor}\sum_{j=1}^{\large\left\lfloor\frac md\right\rfloor}\sum_{k\mid\gcd(i,j)}\mu(k)ij
\]
我们可以枚举 \(k\)。因为 \(k\mid\gcd(i,j)\),\(i,j\) 的上界为 \(\left\lfloor\dfrac nd\right\rfloor\),因此从 \(1\sim\left\lfloor\dfrac nd\right\rfloor\) 枚举。
\[\textit{ans}=\sum_{d=1}^nd\sum_{k=1}^{\large\left\lfloor\frac nd\right\rfloor}\mu(k)\sum_{i=1}^{\large\left\lfloor\frac nd\right\rfloor}\sum_{j=1}^{\large\left\lfloor\frac md\right\rfloor}[k\mid\gcd(i,j)]ij
\]
\(k\mid\gcd(i,j)\) 即 \(k\mid i\land k\mid j\),因此可以枚举 \(i=i'l,j=j'k\):
\[\begin{aligned}
\textit{ans}&=\sum_{d=1}^nd\sum_{k=1}^{\large\left\lfloor\frac nd\right\rfloor}\mu(k)k^2\sum_{i=1}^{\large\left\lfloor\frac n{kd}\right\rfloor}\sum_{j=1}^{\large\left\lfloor\frac m{kd}\right\rfloor}ij\\
&=\sum_{d=1}^nd\sum_{k=1}^{\large\left\lfloor\frac nd\right\rfloor}\mu(k)k^2\left(\sum_{i=1}^{\large\left\lfloor\frac n{kd}\right\rfloor}i\right)\left(\sum_{j=1}^{\large\left\lfloor\frac m{kd}\right\rfloor}j\right)
\end{aligned}
\]
枚举 \(d\) 是 \(\mathcal O(n)\) 的,考虑快速求出后半部分。
若 \(k,d\) 确定,则可以由等差数列求和快速求出 \(\sum_{i=1}\limits^{\large\left\lfloor\frac n{kd}\right\rfloor}i,\sum_{j=1}\limits^{\large\left\lfloor\frac n{kd}\right\rfloor}j\)。
可以考虑进行数论分块,则 \(k\) 在一段区间 \([l,r]\) 内,\(\left\lfloor\dfrac n{kd}\right\rfloor\) 均相同,\(\left\lfloor\dfrac n{kd}\right\rfloor\) 均相同。于是后两项就可以确定。对于 \(\mu(k)k^2\),线性筛后维护一个前缀和即可。
AC 代码
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
typedef long long ll;
#define int long long
constexpr const int N=1e7,P=20101009,inv4=15075757;
int preU[N+1];
void pre(){
static int vis[N+1],prime[N+1],size;
preU[1]=1;
for(int i=2;i<=N;i++){
if(!vis[i]){
prime[++size]=i;
vis[i]=i;
preU[i]=-1;
}
for(int j=1;j<=size&&i*prime[j]<=N;j++){
vis[i*prime[j]]=prime[j];
if(i%prime[j]==0){
break;
}
preU[i*prime[j]]=-preU[i];
}
}
for(int i=1;i<=N;i++){
preU[i]=1ll*preU[i]*i*i%P;
preU[i]+=preU[i-1];
preU[i]%=P;
}
}
ll f(int n,int m){
if(n>m){
swap(n,m);
}
// for(int i=1;i<=n;i++){
// cerr<<"u["<<i<<"]="<<preU[i]-preU[i-1]-i*i<<endl;
// cerr<<"u["<<i<<"]+"<<i<<"^2="<<preU[i]-preU[i-1]<<endl;
// cerr<<"preU["<<i<<"]="<<preU[i]<<endl;
// }
int ans=0;
for(int d=1;d<=n;d++){
int n2=n/d,m2=m/d;
// cerr<<"-------------------------------\nd="<<d<<":\n";
// cerr<<"floor(n/d)="<<n2<<" floor(m/d)="<<m2<<endl;
int ans2=ans;
for(int l=1,r=n2;l<=n2;l=r+1,r=n2){
int tn=n2/l,tm=m2/l;
// cerr<<"tn="<<tn<<" tm="<<tm<<endl;
r=min(n2/tn,m2/tm);
// cerr<<"["<<l<<","<<r<<"]\n";
// cerr<<"preU[r]="<<preU[r]<<" preU[l-1]="<<preU[l-1]<<endl;
// cerr<<"add = "<<1ll*d*(preU[r]-preU[l-1])*inv4%P*tn*(tn+1)%P*tm*(tm+1)%P<<endl;
ans+=1ll*d*(preU[r]-preU[l-1])%P*inv4%P*tn%P*(tn+1)%P*tm%P*(tm+1)%P;
ans%=P;
}
// cerr<<ans-ans2<<endl;
// int add=0;
// for(int k=1;k*d<=n;k++){
// int nn=n/k/d,mm=m/k/d;
// add+=1ll*d*(preU[k]-preU[k-1]-k*k)/*u[k]*/*k%P*k%P*(1+nn)*nn%P%P*(1+mm)*mm%P*inv4%P;
// add%=P;
// }
// cout<<"add in all should be "<<add<<endl;
}
if(ans<0){
ans+=P;
}
return ans;
}
main(){
/*freopen("test.in","r",stdin);
freopen("test.out","w",stdout);*/
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
pre();
int n,m;
cin>>n>>m;
cout<<f(n,m)<<'\n';
cout.flush();
/*fclose(stdin);
fclose(stdout);*/
return 0;
}

浙公网安备 33010602011771号