zoukankan      html  css  js  c++  java
  • [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;
    }
    

  • 相关阅读:
    Java运行环境(win10)
    maven封装jar包遇到的问题
    eclipse安装STS遇到的问题
    Redis IO多路复用的理解
    操作系统文章推荐
    jdk1.8新特性
    Maven笔记
    博主推荐
    MySQL文章推荐
    多线程文章推荐
  • 原文地址:https://www.cnblogs.com/Troverld/p/14619520.html
Copyright © 2011-2022 走看看