LG P3327 [SDOI2015]约数个数和
Description
设 $d(x)$ 为 $x$ 的约数个数,给定 $n,m$,求
$$\sum_{i=1}^n\sum_{j=1}^md(ij)$$
Solution
有等式
$$d(ij)=\sum\limits_{x\mid i}\sum\limits_{y\mid j} [\gcd(x,y)=1]$$
证明:
如果 $ij$ 的因子 $k$ 中有一个因子 $p^c$, $i$ 中有因子 $p^a$, $j$ 中有因子 $p^b$。我们规定:
如果 $c\leqslant a$,那么在 $i$ 中选择。
如果 $c > a$,那么我们把 $c$ 减去 $a$,在 $j$ 中选择 $p^{c-a}$
对于 $ij$ 的因子 $k$ 的其他因子同理。于是对于任何一个 $k$ 有一个唯一的映射,且每一个选择对应着唯一的 $k$。
通过如上过程,我们发现:对于 $ij$ 的因子 $k=\prod {p_i}^{c_i}$,我们不可能同时在 $i$ 和 $j$ 中选择 $p_i$(优先在 $i$ 中选择,如果不够就只在 $j$ 中选择不够的指数),故 $x$ 和 $y$ 必须互质。
开始大力化简原式
\begin{align}
& \sum_{i=1}^n\sum_{j=1}^md(ij)\\
= & \sum\limits_{i=1}^n\sum\limits_{j=1}^m\sum\limits_{x\mid i}\sum\limits_{y\mid j} [\gcd(x,y)=1]\\
= & \sum\limits_{x=1}^n\sum\limits_{y=1}^m \left\lfloor\frac{n}{x}\right\rfloor \left\lfloor\frac{m}{y}\right\rfloor [\gcd(x,y)=1]\\
= & \sum\limits_{i=1}^n\sum\limits_{j=1}^m \left\lfloor\frac{n}{i}\right\rfloor \left\lfloor\frac{m}{j}\right\rfloor[\gcd(i,j)=1]
\end{align}
接下来莫比乌斯反演
设$f(x)=\sum\limits_{i=1}^n\sum\limits_{j=1}^m \left\lfloor\frac{n}{i}\right\rfloor \left\lfloor\frac{m}{j}\right\rfloor[\gcd(i,j)=x],g(x)=\sum_{x\mid d} f(d)$
整理得$g(x)=\sum\limits_{i=1}^{\frac{n}{x}}\sum\limits_{j=1}^{\frac{m}{x}} \left\lfloor\frac{n}{ix}\right\rfloor \left\lfloor\frac{m}{jx}\right\rfloor$
所以题目所求为$f(1)=\sum\limits_{1\mid d}\mu(\frac{d}{1})g(d)=\sum_{i=1}^n \mu(i)g(i)$
预处理$s(x)=\sum\limits_{i=1}^{x} \left\lfloor\frac{x}{i}\right\rfloor$,就可以$O(1)$计算$g$函数
时间复杂度$\Theta(T\sqrt{n})$
#include<iostream> #include<cstdio> #include<cmath> using namespace std; long long T,n,m,s[50005],mu[50005],prime[50005],tot,sum[50005],ans; bool vst[50005]; inline int read() { int f=1,w=0; char ch=0; while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { w=(w<<1)+(w<<3)+ch-'0'; ch=getchar(); } return f*w; } int main() { for(int i=1;i<=50000;i++) { for(int j=1;j<=i;) { int k=i/(i/j); s[i]+=(k-j+1)*(i/j); j=k+1; } } mu[1]=1; for(int i=2;i<=50000;i++) { if(!vst[i]) { prime[++tot]=i; mu[i]=-1; } for(int j=1;j<=tot&&i*prime[j]<=50000;j++) { vst[i*prime[j]]=true; if(!(i%prime[j])) { mu[i*prime[j]]=0; break; } else { mu[i*prime[j]]=-mu[i]; } } } for(int i=1;i<=50000;i++) { sum[i]=sum[i-1]+mu[i]; } T=read(); for(;T;T--) { ans=0; n=read(); m=read(); for(int i=1;i<=min(n,m);) { int j=min(n/(n/i),m/(m/i)); ans+=(sum[j]-sum[i-1])*s[n/i]*s[m/i]; i=j+1; } printf("%lld\n",ans); } return 0; }