题意:
求
∑ni=1∑mj=1lcm(i,j)
∑
i
=
1
n
∑
j
=
1
m
l
c
m
(
i
,
j
)
n,m=1e7,多组输入
O(N) O ( N ) 版本:
遇到lcm首先是换成gcd, Ans=∑ni=1∑mj=1i∗jgcd(i,j) A n s = ∑ i = 1 n ∑ j = 1 m i ∗ j g c d ( i , j ) ,再换成因子角度,设: gcd(i,j)=d,min(n,m)=N g c d ( i , j ) = d , m i n ( n , m ) = N ,有:
观察发现 [Nd] [ N d ] 和 [nid] [ n i d ] 可以整除分块,那么时间复杂度为 O(n−−√∗n−−√)=O(n) O ( n ∗ n ) = O ( n ) ,一般的题目是可以过了,但是案例数多的就不行了
O(N−−√) O ( N ) 版本:
改变枚举变量,令 D=i∗d得: D = i ∗ d 得 :
想办法快速求出
∑i|Du(i)∗i:
∑
i
|
D
u
(
i
)
∗
i
:
代码:
#include<bits/stdc++.h>
#define LL long long
using namespace std;
int read(){int t;
scanf("%d",&t);return t;
}
const int N=1e7+5;
const int mod=1e8+9;//注意模数
LL pri[N>>1],now;
bool vis[N];
LL sum[N];
init(){
now=0;vis[1]=1;
for(LL i=2;i<N;i++){//预处理D*F(D)
if(!vis[i])pri[++now]=i,sum[i]=(i*(1-i)%mod+mod)%mod;
for(LL j=1,mul=2*i;j<=now&&mul<N;j++,mul=pri[j]*i){
vis[mul]=1;
if(i%pri[j]==0){sum[mul]=sum[i]*pri[j]%mod;break;}
sum[mul]=sum[i]*sum[pri[j]]%mod;
}
}
sum[1]=1;
for(int i=2;i<N;i++)sum[i]=(sum[i]+sum[i-1])%mod;//变成前缀数组
}
LL cal(LL x,LL y){
return ((1+x)*x/2%mod)*((1+y)*y/2%mod)%mod;
}
int main(){
init();
int t=read();
while(t--){
LL n=read(),m=read();
LL NN=min(n,m);
LL ans=0;
for(LL l=1,r;l<=NN;l=r+1){
r=min(n/(n/l),m/(m/l));
ans=(ans+(sum[r]-sum[l-1]+mod)%mod*cal(n/l,m/l)%mod)%mod;
}
printf("%lld\n",ans);
}
}