原题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3994
设d(x)为x的约数个数,给定N、M,求 ∑ni=1∑mj=1d(ij) ∑ i = 1 n ∑ j = 1 m d ( i j )
输入文件包含多组测试数据。
第一行,一个整数T,表示测试数据的组数。
接下来的T行,每行两个整数N、M。
T行,每行一个整数,表示你所求的答案。
2
7 4
5 6
110
121
1<=N, M<=50000
1<=T<=50000
初始式子:
这样,原来的式子就变成了:
我们再将 [gcd(x,y)=1] [ g c d ( x , y ) = 1 ] 加以替换(证明)
我们考虑枚举 d d ,因此 d d 要满足 [d|gcd(x,y)] [ d | g c d ( x , y ) ] :
我们考虑直接枚举 d d :
我们再从枚举 i,j i , j 变为枚举 x,y x , y ,那么满足 x|i,y|j x | i , y | j 的 x,y x , y 就分别有 ⌊nx⌋,⌊my⌋ ⌊ n x ⌋ , ⌊ m y ⌋ 个,代入:
进一步的, [d|gcd(x,y)] [ d | g c d ( x , y ) ] 这个判定条件十分碍眼,我们需要消去它。于是,我们不再单纯枚举 x,y x , y ,直接枚举 d d 的倍数 x×d,y×d x × d , y × d :
由于, ⌊ndx⌋ ⌊ n d x ⌋ 只与 n,d,x n , d , x 有关,所以可以提前:
考虑这个形状:
我们可以发现, ⌊ni⌋ ⌊ n i ⌋ 相当于统计 1∼n 1 ∼ n 中,能被 i i 整除的数有多少,那么 ∑ni=1⌊ni⌋ ∑ i = 1 n ⌊ n i ⌋ 就是统计了 1∼n 1 ∼ n 中,所有数的因数有多少个,即 1∼n 1 ∼ n 的因数个数和:
那么,最终形态就变成了:
σ0(x) σ 0 ( x ) 为积性函数,资瓷线筛,因此我们可以预处理前缀和,再利用下底分块,我们便能 O(n−−√) O ( n ) 回答询问。
#include
#define R register int
using namespace std;
const int M=5e4+5,N=5e4;
int miu[M],d[M],p[M],num[M],T;
bool check[M];
void get()
{
R i,j,t;
miu[1]=d[1]=check[1]=1;
for(i=2;i<=N;++i)
{
if(!check[i])miu[i]=-1,d[i]=2,num[i]=1,p[++p[0]]=i;
for(j=1;j<=p[0];++j)
{
t=i*p[j];if(t>N)break;
check[t]=1;
if(i%p[j]==0){miu[t]=0;num[t]=num[i]+1;d[t]=d[i]/(num[i]+1)*(num[i]+2);break;}
miu[t]=-miu[i],num[t]=1,d[t]=d[i]<<1;
}
d[i]+=d[i-1],miu[i]+=miu[i-1];
}
}
long long ans;
void f(int n,int m)
{
R l,r;ans=0;
if(n>m)swap(n,m);
for(l=1;l<=n;l=r+1)r=min(n/(n/l),m/(m/l)),ans+=1ll*(miu[r]-miu[l-1])*d[n/l]*d[m/l];
}
void in(){get();scanf("%d",&T);}
void ac()
{
R a,b,i;
for(i=1;i<=T;++i)
scanf("%d%d",&a,&b),f(a,b),printf("%lld\n",ans);
}
int main()
{
in();ac();
return 0;
}