P3327 [SDOI2015]约数个数和
题目大意
求:
∑ i = 1 n ∑ j = 1 n d ( i × j ) \large\sum\limits_{i=1}^n\sum\limits_{j=1}^nd(i\times j) i=1∑nj=1∑nd(i×j)
其中 d ( i × j ) d(i\times j) d(i×j) 表示 i × j i\times j i×j 的因数个数。
题解
首先证明公式:
d ( i j ) = ∑ x ∣ i ∑ y ∣ j [ gcd ( x , y ) = 1 ] \large d(ij)=\sum\limits_{x\mid i}\sum\limits_{y\mid j}[\gcd(x,y)=1] d(ij)=x∣i∑y∣j∑[gcd(x,y)=1]
证:
我们先考虑 i = p k 1 , j = p k 2 i=p^{k_1},j=p^{k_2} i=pk1,j=pk2,其中 p p p 是质数,现在我们要统计 i j ij ij 有多少种不同的因子,那么我们先考虑 x = 1 x=1 x=1,继续枚举 y y y,发现在 j j j 中可以一直选到 p k 2 p^{k_2} pk2 而做到不重不漏,其中得到的这一堆值分别是 1 , p , p 2 , … , p k 2 1,p,p^2,\ldots,p^{k_2} 1,p,p2,…,pk2。
接下来我不能再继续选取上述的因子,考虑怎么选出 p k 2 + 1 p^{k_2+1} pk2+1 这个因子,显然我只需要让 x = 1 x=1 x=1 变成 x = p x=p x=p,此时我们发现对于 x = p x=p x=p,我们已经不能再继续选取 y = p 0 , … , k 2 y=p^{0,\ldots,k_2} y=p0,…,k2,因为已经被 x = 1 x=1 x=1 选过了,所以 x = p x=p x=p 的贡献是 1 1 1。同理此时我们已经选出了 p k 2 + i p^{k_2+i} pk2+i 这个因子,那我们只有令 x = p i + 1 , y = p k 2 x=p^{i+1},y=p^{k_2} x=pi+1,y=pk2 才能得到 p k 2 + i + 1 p^{k_2+i+1} pk2+i+1 这个因子。
所以简而言之,在这种情况下,我们总共能选取的因子个数是 k 2 + k 1 − 1 k_2+k_1-1 k2+k1−1。此时我们还可以发现,当我们考虑 x ≠ 1 x\neq 1 x=1 时,由于我们永远选取了 y = p k 2 y=p^{k_2} y=pk2,所以我们每个 x x x 只能贡献一次,这一次我不一定是 y = p k 2 y=p^{k_2} y=pk2,如果我们假定 y = 1 y=1 y=1,那么 x ≠ 1 x\neq 1 x=1 的贡献仍然是一次,相当于我们令这个 y = p k 2 y=p^{k_2} y=pk2 的贡献转移给了 y = 1 y=1 y=1。这个时候我们发现我们所有的贡献都来自于 x , y x,y x,y 中至少取了一个 1 1 1。也就是 gcd ( x , y ) = 1 \gcd(x,y)=1 gcd(x,y)=1 的全部情况。
接下来推广到 i = ∏ p i k i , j = ∏ p j k j i=\prod p_i^{k_i},j=\prod p_j^{k_j} i=∏piki,j=∏pjkj。考虑我们在 i i i 中选出了 x = 1 x=1 x=1,此时 y y y 可以取到任何值,且最多只能取到 j j j。此时如果我们想要取到 p x j p_xj pxj,就必须依赖 i i i 中的 p x p_x px,而且这个 p x p_x px 不能与任何 y < j y<j y<j 的 y y y 结合,因为已经被 x = 1 x=1 x=1 考虑过了。所以每一个 x ≠ 1 x\neq 1 x=1 都只会贡献一次 y = j y=j y=j,接下来我们不让 y = j y=j y=j 产生贡献,转移到 y = 1 y=1 y=1 与 x ≠ 1 x\neq 1 x=1 产生贡献。因此发现 x , y x,y x,y 中总是至少选取了一个 1 1 1,故 ∑ x ∣ i ∑ y ∣ j [ gcd ( x , y ) = 1 ] = d ( i j ) \sum\limits_{x\mid i}\sum\limits_{y\mid j}[\gcd(x,y)=1]=d(ij) x∣i∑y∣j∑[gcd(x,y)=1]=d(ij)。
举个例子方便理解,令 i = 2 3 , j = 2 5 i=2^3,j=2^5 i=23,j=25。我们先考虑 x = 1 x=1 x=1 的情况,此时 y y y 可以是 1 , 2 , 4 , 8 , 16 , 32 1,2,4,8,16,32 1,2,4,8,16,32。 i j ij ij 中肯定有 64 64 64 这个因子,只靠 x = 1 x=1 x=1 是枚举不出来的,所以我们将 x = 1 x=1 x=1 改为 x = 2 x=2 x=2,这个时候 y = 1 , 2 , 4 , 8 , 16 y=1,2,4,8,16 y=1,2,4,8,16 的情况都会与 x = 1 x=1 x=1 的情况重合,所以我们不能考虑这些 y y y,因此只有 y = 32 y=32 y=32 的时候我们取到了我们以前没有取到的数 64 64 64。因此对于 x ≠ 1 x\neq 1 x=1,都只有 y = 32 y=32 y=32 产生了贡献,这不方便计算,产生了一次贡献可以视作是 y = 1 y=1 y=1 的贡献,尽管乘起来不是原来没有的因子,但反正我产生了一次贡献,因此我至少会在 x , y x,y x,y 中取一个 1 1 1。对于推广的情况同理。
带入公式得到:
∑ i = 1 n ∑ j = 1 m ∑ x ∣ i ∑ y ∣ j [ gcd ( x , y ) = 1 ] \large\sum\limits_{i=1}^n\sum\limits_{j=1}^m\sum\limits_{x\mid i}\sum\limits_{y\mid j}[\gcd(x,y)=1] i=1∑nj=1∑mx∣i∑y∣j∑[gcd(x,y)=1]
转化 [ n = 1 ] [n=1] [n=1] 的式子:
∑ i = 1 n ∑ j = 1 m ∑ x ∣ i ∑ y ∣ j ∑ d ∣ gcd ( x , y ) μ ( d ) \large\sum\limits_{i=1}^n\sum\limits_{j=1}^m\sum\limits_{x\mid i}\sum\limits_{y\mid j}\sum\limits_{d\mid \gcd(x,y)}\mu(d) i=1∑nj=1∑mx∣i∑y∣j∑d∣gcd(x,y)∑μ(d)
考虑先枚举 x , y x,y x,y:
∑ x = 1 n ∑ y = 1 m ∑ i = 1 n ∑ j = 1 m [ x ∣ i and y ∣ j ] ∑ d ∣ gcd ( x , y ) μ ( d ) \large\sum\limits_{x=1}^n\sum\limits_{y=1}^m\sum\limits_{i=1}^n\sum\limits_{j=1}^m[x\mid i\;\text{and}\;y\mid j]\sum\limits_{d\mid \gcd(x,y)}\mu(d) x=1∑ny=1∑mi=1∑nj=1∑m[x∣iandy∣j]d∣gcd(x,y)∑μ(d)
令 i = i ′ x , j = j ′ y i=i^\prime x,j=j^\prime y i=i′x,j=j′y,得:
∑ x = 1 n ∑ y = 1 m ∑ i = 1 ⌊ n x ⌋ ∑ j = 1 ⌊ m y ⌋ [ x ∣ i x and y ∣ j y ] ∑ d ∣ gcd ( x , y ) μ ( d ) \large\sum\limits_{x=1}^n\sum\limits_{y=1}^m\sum\limits_{i=1}^{\lfloor\frac{n}{x}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{y}\rfloor}[x\mid ix\;\text{and}\;y\mid jy]\sum\limits_{d\mid \gcd(x,y)}\mu(d) x=1∑ny=1∑mi=1∑⌊xn⌋j=1∑⌊ym⌋[x∣ixandy∣jy]d∣gcd(x,y)∑μ(d)
中括号内式子等于 1 1 1,我们直接省去:
∑ x = 1 n ∑ y = 1 m ∑ d ∣ gcd ( x , y ) μ ( d ) ( ⌊ n x ⌋ ⌊ m y ⌋ ) \large\sum\limits_{x=1}^n\sum\limits_{y=1}^m\sum\limits_{d\mid \gcd(x,y)}\mu(d)\left(\left\lfloor\dfrac{n}{x}\right\rfloor\left\lfloor\dfrac{m}{y}\right\rfloor\right) x=1∑ny=1∑md∣gcd(x,y)∑μ(d)(⌊xn⌋⌊ym⌋)
继续拆掉 d ∣ gcd ( x , y ) d\mid\gcd(x,y) d∣gcd(x,y):
∑ d = 1 n ∑ x = 1 n ∑ y = 1 m [ d ∣ x and d ∣ y ] μ ( d ) ( ⌊ n x ⌋ ⌊ m y ⌋ ) \large\sum\limits_{d=1}^n\sum\limits_{x=1}^n\sum\limits_{y=1}^m[d\mid x\;\text{and}\;d\mid y]\mu(d)\left(\left\lfloor\dfrac{n}{x}\right\rfloor\left\lfloor\dfrac{m}{y}\right\rfloor\right) d=1∑nx=1∑ny=1∑m[d∣xandd∣y]μ(d)(⌊xn⌋⌊ym⌋)
令 x = x ′ d , y = y ′ d x=x^\prime d,y=y^\prime d x=x′d,y=y′d,得到:
∑ d = 1 n μ ( d ) ∑ x = 1 ⌊ n d ⌋ ∑ y = 1 ⌊ m d ⌋ ⌊ n x d ⌋ ⌊ m y d ⌋ \large\sum\limits_{d=1}^n\mu(d)\sum\limits_{x=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{y=1}^{\lfloor\frac{m}{d}\rfloor}\left\lfloor\dfrac{n}{xd}\right\rfloor\left\lfloor\dfrac{m}{yd}\right\rfloor d=1∑nμ(d)x=1∑⌊dn⌋y=1∑⌊dm⌋⌊xdn⌋⌊ydm⌋
接下来各回各家:
∑ d = 1 n μ ( d ) ∑ x = 1 ⌊ n d ⌋ ⌊ n x d ⌋ ∑ y = 1 ⌊ m d ⌋ ⌊ m y d ⌋ \large\sum\limits_{d=1}^n\mu(d)\sum\limits_{x=1}^{\lfloor\frac{n}{d}\rfloor}\left\lfloor\dfrac{n}{xd}\right\rfloor\sum\limits_{y=1}^{\lfloor\frac{m}{d}\rfloor}\left\lfloor\dfrac{m}{yd}\right\rfloor d=1∑nμ(d)x=1∑⌊dn⌋⌊xdn⌋y=1∑⌊dm⌋⌊ydm⌋
令 f ( n ) f(n) f(n) 表示 ∑ i = 1 n ⌊ n i ⌋ \sum\limits_{i=1}^n\left\lfloor\dfrac{n}{i}\right\rfloor i=1∑n⌊in⌋,得到:
∑ d = 1 n μ ( d ) f ( ⌊ n d ⌋ ) f ( ⌊ m d ⌋ ) \large\sum\limits_{d=1}^n\mu(d)f\left(\left\lfloor\dfrac{n}{d}\right\rfloor\right)f\left(\left\lfloor\dfrac{m}{d}\right\rfloor\right) d=1∑nμ(d)f(⌊dn⌋)f(⌊dm⌋)
内层的 f f f 函数可以整除分块解决,但为了不让每次询问都进行一次 O ( n ) \mathcal{O}(\sqrt{n}) O(n) 的运算,我们提前算好这个前缀和。剩下的每次查询也是整除分块,提前预处理 μ ( d ) \mu(d) μ(d) 的前缀和即可,总时间复杂度 O ( T n ) \mathcal{O}(T\sqrt{n}) O(Tn)。
代码
//P3327
//你 cnt=1 了吗?
#include<bits/stdc++.h>
#define int long long
#define mid ((l+r)>>1)
#define fir first
#define sec second
#define lowbit(i) (i&(-i))
using namespace std;
const int N=5e4+5;
const int inf=1e18;
struct edge{int to,nxt,l;};
inline int read(){
char op=getchar();
int w=0,s=1;
while(op<'0'||op>'9'){
if(op=='-') s=-1;
op=getchar();
}
while(op>='0'&&op<='9'){
w=(w<<1)+(w<<3)+op-'0';
op=getchar();
}
return w*s;
}
//数论部分
const double pi=acos(-1);
const int mod=1e9+7;
int Mul(int a,int b){return (a%mod*b%mod)%mod;}
int Add(int a,int b){return (a+b)%mod;}
int Dec(int a,int b){return (a-b+mod)%mod;}
int Pow(int a,int k){
int ans=1;
while(k){
if(k&1) ans=Mul(ans,a);
a=Mul(a,a);
k>>=1;
}
return ans;
}
int gcd(int x,int y){return y==0?x:gcd(y,x%y);}
int lcm(int x,int y){return x/gcd(x,y)*y;}
int inv(int x){return Pow(x,mod-2);}
void exgcd(int a,int b,int &x,int &y){
if(b==0){
x=1,y=0;
return;
}
exgcd(b,a%b,x,y);
int t=x;
x=y;
y=t-a/b*y;
}
int mu[N],vis[N],p[N],cnt=0,ans[N],sum[N];
void mobius(){
mu[1]=1;
for(register int i=2;i<=N-5;i++){
if(!vis[i]){
mu[i]=-1;
p[++cnt]=i;
}
for(register int j=1;j<=cnt&&i*p[j]<=N-5;j++){
vis[i*p[j]]=1;
if(i%p[j]==0){
mu[i*p[j]]=0;
break;
}
mu[i*p[j]]=-mu[i];
}
}
for(register int i=1;i<=N-5;i++) sum[i]=sum[i-1]+mu[i];
for(register int i=1;i<=N-5;i++){//预处理整除分块
for(register int l=1,r;l<=i;l=r+1){
r=i/(i/l);
ans[i]+=(i/l)*(r-l+1);
}
}
}
int Sqrt(int n,int m){
int tot=0;
for(register int l=1,r;l<=min(n,m);l=r+1){
r=min(n/(n/l),m/(m/l));
tot+=(sum[r]-sum[l-1])*ans[n/l]*ans[m/l];
}
return tot;
}
signed main(){
int T=read();
mobius();
while(T--){
int n=read(),m=read();
printf("%lld\n",Sqrt(n,m));
}
}