iv.[SDOI2015]约数个数和
完蛋了,我们前几题里面都有\(\gcd(\dots)\),但是这道题没有,怎么办呢?
引理:
\(\boxed{d(ij)=\sum\limits_{x|i}\sum\limits_{y|j}[\gcd(x,y)==1]}\)
换句话说,两个数\((i,j)\)积的因数个数,等于\(i\)的所有因数与\(j\)的所有因数中互质的对数。
简单证明(感谢suncongbo巨佬的讲解):
显然各质因数之间相互独立(最后是个数乘在一起,只要每一项都相等,乘积也就相等),我们不如只考虑一个质因数\(p\)。设它在\(i\)中的次数是\(a\),在\(j\)中的次数是\(b\)。那么,在乘积\(i\!j\)中,次数就是\(a+b\)。
考虑仅\(p\)一项为\(i\!j\)贡献了多少因数。显然,是\(a+b+1\)个,次数从\(0\)一直到\(i+j\)。
那么为等式右侧贡献了多少呢?当\(i\)中不取任何\(p\)时,\(j\)中可以取\(1\)到\(b\)个\(p\),共\(b\)种;当\(j\)中不取任何\(p\)时,\(i\)中可以取\(1\)到\(a\)个\(p\),共\(a\)种;除此之外,还有一种\(i\)和\(j\)都不选的,共\(1\)种。综上,为等式右侧它也贡献了\(a+b+1\)。
因为对于每一个单独的\(p\),等式左右的贡献相等;所以综合起来考虑,等式左右贡献的积也相等。
证毕。
然后我们看一看套上这个东西后我们要求什么:
\(ans=\sum\limits_{i=1}^n\sum\limits_{j=1}^m\sum\limits_{x|i}\sum\limits_{y|j}[\gcd(x,y)==1]\)。
这个时候,我们就应该想想有没有可能调换枚举顺序。
优先枚举\(x\)和\(y\),则对于每个\(x\),有\(\dfrac{n}{x}\)个这样的\(i\),满足\(i\leq n\)并且\(x|i\)。对于每个\(y\),则有\(\dfrac{m}{y}\)个这样的\(j\)。
因此有\(ans=\sum\limits_{i=1}^n\sum\limits_{j=1}^m\left\lfloor\dfrac{n}{i}\right\rfloor\left\lfloor\dfrac{m}{j}\right\rfloor[\gcd(i,j)==1]\)。
老套路,设\(f(x)=\sum\limits_{i=1}^n\sum\limits_{j=1}^m\left\lfloor\dfrac{n}{i}\right\rfloor\left\lfloor\dfrac{m}{j}\right\rfloor[\gcd(i,j)==x]\),\(g(x)=\sum\limits_{x|d}f(d)\)。
反演一下,得\(f(x)=\sum\limits_{x|d}g(d)\mu(\dfrac{d}{n})\)。
我们要求的是\(f(1)\),\(f(1)=\sum\limits_{x|1}g(d)\mu(\dfrac{d}{1})=\sum\limits_{x=1}^{\infty}g(d)\mu(d)\)。
我们看一下\(g(d)\)是什么。
有\(g(x)=\sum\limits_{x|d}f(d)=\sum\limits_{x|d}\sum\limits_{i=1}^n\sum\limits_{j=1}^m\left\lfloor\dfrac{n}{i}\right\rfloor\left\lfloor\dfrac{m}{j}\right\rfloor[\gcd(i,j)==d]\)
发现这个\(d\)可以是任何\(x\)的倍数。因此有\(g(x)=\sum\limits_{i=1}^n\sum\limits_{j=1}^m\left\lfloor\dfrac{n}{i}\right\rfloor\left\lfloor\dfrac{m}{j}\right\rfloor[x|\gcd(i,j)]\)
我们将\(x\)除出来,有
\(g(x)=\sum\limits_{i=1}^{\frac{n}{x}}\sum\limits_{j=1}^{\frac{m}{x}}\left\lfloor\dfrac{n}{ix}\right\rfloor\left\lfloor\dfrac{m}{jx}\right\rfloor[1|\gcd(i,j)]=\sum\limits_{i=1}^{\frac{n}{x}}\sum\limits_{j=1}^{\frac{m}{x}}\left\lfloor\dfrac{n}{ix}\right\rfloor\left\lfloor\dfrac{m}{jx}\right\rfloor\)
我们令一个\(sum(x)=\sum\limits_{i=1}^{x}\left\lfloor\dfrac{x}{i}\right\rfloor\),这个东西可以在\(O(n\sqrt{n})\)时间内通过整除分块写出来。
则\(g(x)=sum(\dfrac{n}{x})*sum(\dfrac{m}{x})\)。
又有\(ans=\sum\limits_{x=1}^{\infty}g(d)\mu(d)\),这东西也可以整除分块,因为里面有一个可以整除分块的\(g(d)\)和一个可以求前缀和的\(\mu(d)\)。
然后就可以了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int T,n,m,mu[50100],pri[50100],sum[50100],res;
void getmu(int N){
mu[1]=1;
for(int i=2;i<=N;i++){
if(!pri[i])pri[++pri[0]]=i,mu[i]=-1;
for(int j=1;j<=pri[0]&&i*pri[j]<=N;j++){
pri[i*pri[j]]=true;
if(!(i%pri[j]))break;
mu[i*pri[j]]=-mu[i];
}
}
for(int i=1;i<=N;i++)mu[i]+=mu[i-1];
}
void getsum(int N){
for(int i=1;i<=N;i++)for(int l=1,r;l<=i;l=r+1)r=i/(i/l),sum[i]+=(r-l+1)*(i/l);
}
signed main(){
scanf("%lld",&T),getmu(50000),getsum(50000);
while(T--){
scanf("%lld%lld",&n,&m);
if(n>m)swap(n,m);
res=0;
for(int l=1,r;l<=n;l=r+1)r=min(n/(n/l),m/(m/l)),res+=(mu[r]-mu[l-1])*sum[n/l]*sum[m/l];
printf("%lld\n",res);
}
return 0;
}