洛谷 P1829 [国家集训队]Crash的数字表格 / JZPTAB
题目链接
先把式子写出来~
\(ans=\sum\limits_{i=1}^n{\sum\limits_{j=1}^m{lcm(i,j)}}\)
\(=\sum\limits_{i=1}^n{\sum\limits_{j=1}^m{\frac{ij}{gcd(i,j)}}}\)
\(=\sum\limits_{d=1}{\sum\limits_{i=1}^n{\sum\limits_{j=1}^m{\frac{ij}{d}[gcd(i,j)=d]}}}\)
\(=\sum\limits_{d=1}{\frac{1}{d}\sum\limits_{i=1}^n{\sum\limits_{j=1}^m{ij[gcd(i,j)=d]}}}\)
于是设
\(f(d)=\sum\limits_{i=1}^n{\sum\limits_{j=1}^m{ij[gcd(i,j)=d]}}\)
\(F(d)=\sum\limits_{i=1}^n{\sum\limits_{j=1}^m{ij[d \mid gcd(i,j)]}}\)
显然
\(F(d)=\sum\limits_{d \mid d'}f(d')=\sum\limits_{i=1}^{\frac{n}{d}}{\sum\limits_{j=1}^{\frac{m}{d}}{ijd^2}}\)
反演
\(f(d)=\sum\limits_{d \mid d'}{\mu(\frac{d'}{d})F(d')}\)
\(\therefore ans=\sum\limits_{d=1}{\frac{1}{d}f(d)}\)
\(=\sum\limits_{d=1}{\frac{1}{d}\sum\limits_{d \mid d'}{\mu(\frac{d'}{d})F(d')}}\)
\(=\sum\limits_{d=1}{\frac{1}{d}\sum\limits_{k=1}{\mu(k)F(dk)}}\)
\(=\sum\limits_{d=1}{\frac{1}{d}\sum\limits_{k=1}{\mu(k)\sum\limits_{i=1}^{\frac{n}{dk}}{\sum\limits_{j=1}^{\frac{m}{dk}}{ijd^2k^2}}}}\)
\(=\sum\limits_{d=1}{d\sum\limits_{k=1}{k^2\mu(k)\sum\limits_{i=1}^{\frac{n}{dk}}{\sum\limits_{j=1}^{\frac{m}{dk}}{ij}}}}\)
所以把自然数的前缀和及\(k^2\mu(k)\)预处理出来即可分块套分块解决,时间复杂度\(O(n)\)。
\({\frak{code:}}\)
#include<bits/stdc++.h>
#define IL inline
using namespace std;
const int N=1e7+3,p=20101009;
int n,m,ans,sum[N],mu[N],vis[N],pri[N];
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL void mod(int &x){if(x>=p) x-=p;}
IL void Mod(int &x){if(x<0) x+=p;}
void pre(int n){
mu[1]=sum[1]=1;
for(int i=2;i<=n;++i){
mod(sum[i]=sum[i-1]+i);
if(!vis[i]) pri[++pri[0]]=i,mu[i]=-1;
for(int j=1,k;(k=i*pri[j])<=n;++j){
vis[k]=1;
if(i%pri[j]) mu[k]=-mu[i];
else break;
}
}
for(int i=1;i<=n;++i) mu[i]=1ll*mu[i]*i*i%p,mod(mu[i]+=mu[i-1]),Mod(mu[i]);
}
IL int get(int n,int m){
int res=0;
for(int l=1,r;l<=n;l=r+1){
r=min(n/(n/l),m/(m/l));
mod(res+=1ll*sum[n/l]*sum[m/l]%p*(mu[r]-mu[l-1]+p)%p);
}
return res;
}
int main()
{
n=in(),m=in();
if(n>m) swap(n,m);
pre(m);
for(int l=1,r;l<=n;l=r+1){
r=min(n/(n/l),m/(m/l));
mod(ans+=1ll*get(n/l,m/l)*(sum[r]-sum[l-1]+p)%p);
}
cout<<(ans%p+p)%p<<endl;
return 0;
}