虽然题目上写了反演但是我不知道什么是反演……如果你把Sigma调换位置叫做反演的话。
这道题题面非常简单:
设d(x)d(x)d(x)为正整数xxx的约数个数,给定N,MN, MN,M,
求∑i=1N∑j=1Md(ij)\sum_{i=1}^N\sum_{j=1}^Md(ij)i=1∑Nj=1∑Md(ij)
当时我too naive,看到这玩意就默默地打50分暴力去了。。。
今天江苏神犇们做了这道题,我顺便听明白了~~
首先它不知用什么精妙的办法(可能我以后会知道),推出了
∑i=1N∑j=1Md(ij)=∑i=1N∑j=1M⌊Ni⌋⌊Mi⌋[gcd(i,j)==1]\sum_{i=1}^N\sum_{j=1}^Md(ij)=
\sum_{i=1}^N\sum_{j=1}^M\left\lfloor\frac Ni\right\rfloor\left\lfloor\frac Mi\right\rfloor[gcd(i,j)==1]i=1∑Nj=1∑Md(ij)=i=1∑Nj=1∑M⌊iN⌋⌊iM⌋[gcd(i,j)==1]
证明办法是做二阶差分,
记
F(N,M):=∑i=1N∑j=1Md(ij) ,G(N,M):=∑i=1N∑j=1M⌊Ni⌋⌊Mi⌋[gcd(i,j)==1]=∑i=1∞∑j=1∞⌊Ni⌋⌊Mi⌋[gcd(i,j)==1] (这两个等价,∵∀i(>N),⌊N/i⌋==0)\begin{aligned}
F(N,M)&:=\sum_{i=1}^N\sum_{j=1}^Md(ij)\, ,\\
G(N,M)&:=\sum_{i=1}^N\sum_{j=1}^M\left\lfloor\frac Ni\right\rfloor\left\lfloor\frac Mi\right\rfloor[gcd(i,j)==1] \\
&=\sum_{i=1}^{\infty}\sum_{j=1}^\infty\left\lfloor\frac Ni\right\rfloor\left\lfloor\frac Mi\right\rfloor[gcd(i,j)==1]\,(这两个等价,\because \forall i(>N), \lfloor N/i\rfloor==0)
\end{aligned}F(N,M)G(N,M):=i=1∑Nj=1∑Md(ij),:=i=1∑Nj=1∑M⌊iN⌋⌊iM⌋[gcd(i,j)==1]=i=1∑∞j=1∑∞⌊iN⌋⌊iM⌋[gcd(i,j)==1](这两个等价,∵∀i(>N),⌊N/i⌋==0)
则(二阶差分在这里)
d(N×M)=F(N,M)−F(N−1,M)−F(N,M−1)+F(N−1,M−1)\begin{aligned}
d(N\times M)=F(N,M)&-F(N-1,M)\\
&-F(N,M-1)+F(N-1,M-1)
\end{aligned}d(N×M)=F(N,M)−F(N−1,M)−F(N,M−1)+F(N−1,M−1)
G(N,M)−G(N−1,M)−G(N,M−1)+G(N−1,M−1)=∑i=1∞∑j=1∞(⌊Ni⌋−⌊N−1i⌋)(⌊Mi⌋−⌊M−1i⌋)[gcd(i,j)==1]\begin{aligned}
&G(N,M)-G(N-1,M)-G(N,M-1)+G(N-1,M-1)
\\=&\sum_{i=1}^{\infty}\sum_{j=1}^\infty
\left(\left\lfloor\frac Ni\right\rfloor-\left\lfloor\frac {N-1}i\right\rfloor\right)
\left(\left\lfloor\frac Mi\right\rfloor-\left\lfloor\frac {M-1}i\right\rfloor\right)
[gcd(i,j)==1]
\end{aligned}=G(N,M)−G(N−1,M)−G(N,M−1)+G(N−1,M−1)i=1∑∞j=1∑∞(⌊iN⌋−⌊iN−1⌋)(⌊iM⌋−⌊iM−1⌋)[gcd(i,j)==1]
考虑G的二阶差分的意义。
(⌊Ni⌋−⌊N−1i⌋)\left(\left\lfloor\frac Ni\right\rfloor-\left\lfloor\frac {N-1}i\right\rfloor\right)(⌊iN⌋−⌊iN−1⌋)只有当i∣Ni|Ni∣N的时候才为111,(⌊Mi⌋−⌊M−1i⌋)\left(\left\lfloor\frac Mi\right\rfloor-\left\lfloor\frac {M-1}i\right\rfloor\right)(⌊iM⌋−⌊iM−1⌋)同理。
即G的二阶差分可理解为满足1≤i≤N,1≤j≤M,gcd(i,j)==1,且i∣N,j∣M1\le i \le N,1 \le j \le M,gcd(i,j)==1,且i|N,j|M1≤i≤N,1≤j≤M,gcd(i,j)==1,且i∣N,j∣M的有序数对(i,j)(i,j)(i,j)的个数。由于i,ji, ji,j分别是N,MN, MN,M的约数,因此对于任意两个不同的质因子p,qp, qp,q,i,ji, ji,j的选择方案是互不影响的。
因此,可考虑N×MN\times MN×M的任一质因子ppp,设x,yx, yx,y分别是满足px∣i,py∣jp^x|i,p^y|jpx∣i,py∣j的最大整数。可以得出,质因子ppp在等式左边的选法数为x+y+1x+y+1x+y+1,而在等式右边的选法数也是x+y+1x+y+1x+y+1(当i,ji,ji,j其中任意一者含有质因子ppp时,另一个就不可以含有质因子ppp)。
故等式成立。
继续对该式进行变换(不失一般性,设 N≤MN\le MN≤M),
F(N,M)=G(N,M)=∑i=1N∑j=1M⌊Ni⌋⌊Mi⌋[gcd(i,j)==1]=∑i=1N∑j=1M⌊Ni⌋⌊Mi⌋∑d∣gcd(i,j)μ(d)=∑d=1Nμ(d)∑i=1⌊N/d⌋∑j=1⌊M/d⌋⌊Nid⌋⌊Mjd⌋=∑d=1Nμ(d)∑i=1⌊N/d⌋⌊Nid⌋∑j=1⌊M/d⌋⌊Mjd⌋\begin{aligned}
F(N,M)&=G(N,M) \\
&=\sum_{i=1}^N\sum_{j=1}^M\left\lfloor\frac Ni\right\rfloor\left\lfloor\frac Mi\right\rfloor[gcd(i,j)==1] \\ &=\sum_{i=1}^N\sum_{j=1}^M\left\lfloor\frac Ni\right\rfloor\left\lfloor\frac Mi\right\rfloor\sum_{d|gcd(i,j)}\mu(d) \\
&=\sum_{d=1}^N\mu(d)\sum_{i=1}^{\lfloor N/d\rfloor}\sum_{j=1}^{\lfloor M/d\rfloor}\left\lfloor\frac N{id}\right\rfloor\left\lfloor\frac M{jd}\right\rfloor \\ &=\sum_{d=1}^N\mu(d)\sum_{i=1}^{\lfloor N/d\rfloor}\left\lfloor\frac N{id}\right\rfloor\sum_{j=1}^{\lfloor M/d\rfloor}\left\lfloor\frac M{jd}\right\rfloor
\end{aligned} F(N,M)=G(N,M)=i=1∑Nj=1∑M⌊iN⌋⌊iM⌋[gcd(i,j)==1]=i=1∑Nj=1∑M⌊iN⌋⌊iM⌋d∣gcd(i,j)∑μ(d)=d=1∑Nμ(d)i=1∑⌊N/d⌋j=1∑⌊M/d⌋⌊idN⌋⌊jdM⌋=d=1∑Nμ(d)i=1∑⌊N/d⌋⌊idN⌋j=1∑⌊M/d⌋⌊jdM⌋
记g(n):=∑i=1n⌊ni⌋g(n):=\sum_{i=1}^n\left\lfloor\frac ni\right\rfloorg(n):=i=1∑n⌊in⌋
则F(N,M)F(N,M)F(N,M)可进一步化简为F(N,M)=∑d=1Nμ(d)g(⌊Nid⌋)g(⌊Mjd⌋)F(N,M)=\sum_{d=1}^N\mu(d)g\left(\left\lfloor\frac N{id}\right\rfloor\right)g\left(\left\lfloor\frac M{jd}\right\rfloor\right)F(N,M)=d=1∑Nμ(d)g(⌊idN⌋)g(⌊jdM⌋)
可以发现ggg函数其实就是ddd函数的前缀和,于是可以线性筛出来。
至于约数个数怎么线性筛,只要记录一下每个数,他的素因子分解式中最小素因子的次数c[i]
即可。
void genPrime(int n){
memset(isprime,true,sizeof isprime);
mu[1]=d[1]=c[1]=1;
for(int i=2;i<=n;++i){
if(isprime[i]){
prime[tot++]=i;
mu[i]=-1;
c[i]=1;
d[i]=2;
}
for(int j=0;j<tot&&i*prime[j]<=n;++j){
isprime[i*prime[j]]=false;
if(i%prime[j]==0){
d[i*prime[j]]=d[i]/(c[i]+1)*(c[i]+2);
c[i*prime[j]]=c[i]+1;break;
}
mu[i*prime[j]]=-mu[i];
d[i*prime[j]]=d[i]*d[prime[j]];
c[i*prime[j]]=1;
}
}
}
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long ll;
int gcd(int a,int b){return b?gcd(b,a%b):a;}
const int maxn=50001;
int tot,prime[30001],mu[maxn],c[maxn];
ll d[maxn];
bool isprime[maxn];
ll Ans[101][101];
void genPrime(int n){
memset(isprime,true,sizeof isprime);
mu[1]=d[1]=c[1]=1;
for(int i=2;i<=n;++i){
if(isprime[i]){
prime[tot++]=i;
mu[i]=-1;
c[i]=1;
d[i]=2;
}
for(int j=0;j<tot&&i*prime[j]<=n;++j){
isprime[i*prime[j]]=false;
if(i%prime[j]==0){
d[i*prime[j]]=d[i]/(c[i]+1)*(c[i]+2);
c[i*prime[j]]=c[i]+1;break;
}
mu[i*prime[j]]=-mu[i];
d[i*prime[j]]=d[i]*d[prime[j]];
c[i*prime[j]]=1;
}
}
for(int i=1;i<=n;++i) mu[i]+=mu[i-1];
for(int i=1;i<=n;++i) d[i]+=d[i-1];
}
inline int read(){
int x=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=x*10+ch-48,ch=getchar();
return x;
}
char a[18];
inline void print(ll x){
a[0]=0;
while(x) a[++a[0]]=x%10,x/=10;
for(;a[0];--a[0])
putchar(a[a[0]]+48);
putchar('\n');
}
int main(){
genPrime(50000);
int t=read();
while(t--){
int n=read(),m=read();
if(n<=100&&m<=100){
if(Ans[n][m]){
print(Ans[n][m]);
continue;
}
}
if(n>m) swap(n,m);
ll ans=0;
for(int i=1,nex;i<=n;i=nex+1){
nex=min(n/(n/i),m/(m/i));
ans+=(mu[nex]-mu[i-1])*d[n/i]*d[m/i];
}
if(n<=100&&m<=100) Ans[n][m]=Ans[m][n]=ans;
print(ans);
}
return 0;
}