神题……不会做啊……抄题解吧……
以下题解来自网上(某爬虫网站的我也不知道原作者是谁)
下边均设n<=m
∑i=1n∑j=1mlcm(i,j)=∑i=1n∑j=1mij(i,j)
然后我们要想枚举d=(i,j),那么就要确定ij怎么取,显然我们只需要先除去i和
F(x,y)=∑i=1x∑j=1yij[(i,j)=1]
那么原式变成
∑d=1nd2F(⌊nd⌋,⌊md⌋)d=∑d=1ndF(⌊nd⌋,⌊md⌋)
考虑求F(x,y)
F(x,y)=∑i=1x∑j=1yij[(i,j)=1]=∑i=1x∑j=1yij∑d|(i,j)μ(d)=∑d=1xμ(d)∑d|ixi∑d|jyj=∑d=1xμ(d)d2∑i=1⌊xd⌋∑j=1⌊yd⌋1=∑d=1xμ(d)d2⌊xd⌋(⌊xd⌋+1)2⌊yd⌋(⌊yd⌋+1)2
带回原式得
==∑d=1ndF(⌊nd⌋,⌊md⌋)∑d=1nd∑i=1⌊nd⌋μ(i)i2⌊⌊nd⌋i⌋(⌊⌊nd⌋i⌋+1)2⌊⌊md⌋i⌋(⌊⌊md⌋i⌋+1)2∑d=1nd∑i=1⌊nd⌋μ(i)i2⌊ndi⌋(⌊ndi⌋+1)2⌊mdi⌋(⌊mdi⌋+1)2
现在已经可以O(n−√n−√)=O(n)单次查询了,但是不够理想,我们继续化简
令T=di,则i|T,d=T/i,换掉指标,得
==∑d=1nd∑i=1⌊nd⌋μ(i)i2⌊ndi⌋(⌊ndi⌋+1)2⌊mdi⌋(⌊mdi⌋+1)2∑T=1n⌊nT⌋(⌊nT⌋+1)2⌊mT⌋(⌊mT⌋+1)2∑i|TTiμ(i)i2∑T=1n⌊nT⌋(⌊nT⌋+1)2⌊mT⌋(⌊mT⌋+1)2T∑i|Tμ(i)i
设g(T)=T∑i|Tμ(i)i,我们再设f(T)=∑i|Tμ(i)i那么g(T)=Tf(T),考虑求f(T)
在线性筛中,外层为k,内层为
当py|k时
当i取的数的因子中不包含新加入的
当i取包含新加入的因子
综上,当py|k时,答案为f(k)
当py∤k时
当i取的数的因子中不包含新加入的
当i取的数的因子包含新加入的
====∑i|Tμ(i)i∑apy|kpyμ(apy)apypy∑a|kμ(a)μ(py)a−py∑a|kμ(a)a−pyf(k)
综上,当py∤k时,答案为(1−py)f(k)
然后线性筛随便搞搞即可,最后答案就是
∑T=1n⌊nT⌋(⌊nT⌋+1)2⌊mT⌋(⌊mT⌋+1)2g(T)
F(x,y)=∑i=1x∑j=1yij[(i,j)=1]=∑i=1x∑j=1yij∑d|(i,j)μ(d)
感觉这一步转化好神啊,完全想不到。
#include<iostream>
#include<cstdio>
#define ll long long
#define M 100000009
#define N 10000005
using namespace std;
int prime[N],mobius[N];
bool flag[N];
ll f[N];
inline ll read()
{
ll a=0,f=1; char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
return a*f;
}
inline void prepare()
{
mobius[1]=1; f[1]=1;
for (int i=2;i<N;i++)
{
if (!flag[i]) prime[++prime[0]]=i,mobius[i]=-1,f[i]=1-i;
for (int j=1;j<=prime[0]&&i*prime[j]<N;j++)
{
flag[i*prime[j]]=1;
if (i%prime[j]==0) {mobius[i*prime[j]]=0; f[i*prime[j]]=f[i]; break;}
mobius[i*prime[j]]=-mobius[i]; f[i*prime[j]]=(f[i]*f[prime[j]])%M;
}
}
for (int i=1;i<N;i++)
f[i]=(f[i-1]+f[i]*i%M)%M;
}
inline ll sum(ll n,ll m)
{
return (n*(n+1)/2%M)*(m*(m+1)/2%M)%M;
}
inline ll work(ll n,ll m)
{
ll ans=0;
for (ll i=1,pos;i<=n;i=pos+1)
{
pos=min(n/(n/i),m/(m/i));
ans=(ans+(f[pos]-f[i-1]+M)%M*sum(n/i,m/i)%M)%M;
}
return ans;
}
int main()
{
int testcase=read();
prepare();
while (testcase--)
{
ll n=read(),m=read();
if (n>m) swap(n,m);
printf("%lld\n",(work(n,m)+M)%M);
}
return 0;
}