[SDOI2015]约数个数和

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;
}

上一篇:highcharts实例教程一:结合php与mysql生成折线图


下一篇:mysql启动停止,一台服务器跑 多个mysql数据库