化神奇为腐朽的数论—— 能量采集 总结

Sweetlemon

2018-10-18 20:47:00

Solution

什么时候我这种蒟蒻也开始看$\text{NOI}$的题了呢?(逃 好吧,也是[$\text{hkr04}$](https://www.luogu.org/space/show?uid=111528)这位神犇介绍我看的,而看到这道题之前[$\text{HKZeril}$](https://www.luogu.org/space/show?uid=66073)神犇把与这题有关的一个结论介绍给了我。在此向这两位神犇表示深深的谢意! 好的,言归正传。我们先看一个结论(定理)。~~这次不会是什么恋爱循环定理啦~~ 定理 $2.33$ 设$A(0,0),B(n,m)(n,m\in \mathbb{N}_{+})$,那么线段$AB$上整点的个数即为$\gcd(n,m)+1$。 如何证明这个定理呢? 设点$C(x,y)(x,y\in \mathbb{N})$在线段$AB$上,那么有$\frac{x}{n}=\frac{y}{m}$。 设$g=\gcd(n,m),n'=\frac{n}{g},m'=\frac{m}{g}$。则上式可化为$\frac{x}{n'}=\frac{y}{m'}$,则$xm'=yn'$。 由$n'\perp m'$,得$n'\mid x,m'\mid y$。现在可设$k=\frac{x}{n'}=\frac{y}{m'}(k\in \mathbb{N})$,又由线段的条件知$0\le k \le g$。 我们构造一个点集$S=\{(kn',km')\mid k\in \mathbb{N}, 0\le k\le g\}$,可以发现点集中的点全部都在线段$AB$上。又根据上面的讨论知道线段$AB$上的整点必须满足集合$S$的条件,因此我们就可以下定论:$S$是线段$AB$上的所有整点的集合。易知$\left| S \right|=g+1$。定理证毕。 现在请看题(逃 [P1447 [NOI2010]能量采集](https://www.luogu.org/problemnew/show/P1447) 读题并结合定理,这道题要求的就是$$2\sum^{n}_{i=1}\sum^{m}_{j=1}(\gcd(i,j))-nm$$ (注意这题的线段$AB$不含端点,因此每一条线段上的整点数应为$\gcd(i,j)-1$。) 上式的难点就在于$\sum^{n}_{i=1}\sum^{m}_{j=1}\gcd(i,j)$。下面请看一些神奇的推导! 首先,我们再看一个定理: 定理$2.333$ $\text{ }\varphi$函数对约数求和定理 (《数论入门》$\text{P35}$) $$\forall n\in \mathbb{N_{+}},\sum_{d\mid n}\varphi(d)=n$$ 证明:根据$\varphi$函数的定义,$\varphi(n)$表示$[1,n]$中与$n$互质的整数的数目。 设$d\mid n,n=kd(k\in\mathbb{N})$,设集合$S=\{x\in\mathbb{N_{+}}\mid x\perp d\}$,则$\left| S\right|=\varphi (d)$。设函数$f:S\rightarrow \mathbb{N}$,$f(x)=kx$。设$f(x)$的值域为$S'$。由于$f$是单射,因此$\left|S'\right|=\left|S\right|=\varphi(d)$。 对于$S'$中的每一个元素$f(x)=kx$,由于$x\perp d$,因此$\gcd(f(x),n)=\gcd(kx,kd)=k\gcd(x,d)=k$。 因此,我们可以说,$S'=\{x\in\mathbb{N_{+}}\mid \gcd(x,n)=k\}$。 $\{k\}$和$\{d\}$中的元素是一一对应的,且不同$k$的$S'$两两不交,因此$\bigcup S'$即为$[1,n]\cap\mathbb{N}$,定理证毕。 根据这个定理,我们可以对$\sum^{n}_{i=1}\sum^{m}_{j=1}\gcd(i,j)$这个式子做一些似乎无意义的变形:$$\sum^{n}_{i=1}\sum^{m}_{j=1}\gcd(i,j)=\sum^{n}_{i=1}\sum^{m}_{j=1}\sum_{d\mid \gcd(i,j)}\varphi (d)=\sum^{n}_{i=1}\sum^{m}_{j=1}\sum_{d\mid i,d\mid j}\varphi (d)$$ 然而,这一步变形却是$\text{AC}$的开始。接下来我们考虑更换枚举顺序(即交换求和号的顺序)。 考虑对于每一个数$d$,$\varphi(d)$会被加几次呢?也就是说,在$1\le i\le n,1\le j\le m$的范围内,有多少对$(n,m)$满足$d\mid i,d\mid j$呢? 在范围内,$i$有$\lfloor \frac{n}{d} \rfloor$种选法,$j$有$\lfloor \frac{m}{d} \rfloor$种选法,根据乘法原理,满足条件的数对就有$\lfloor \frac{n}{d} \rfloor\lfloor \frac{m}{d} \rfloor$个。于是$\varphi(d)$对答案的贡献便是$\lfloor \frac{n}{d} \rfloor\lfloor \frac{m}{d} \rfloor \varphi(d)$。 写到这里,答案已经呼之欲出了,便是 $$\sum^{\min(n,m)}_{i=1}\lfloor \frac{n}{d} \rfloor\lfloor \frac{m}{d} \rfloor \varphi(d)$$ 接下来的事情便十分简单,使用欧拉筛(线性筛)计算出$\varphi$函数的值,再直接求和即可。时间复杂度$\text{O}(n)$。 当然,推导到这一步需要很强的思维能力。为了简化思维难度,大佬们研发出了莫比乌斯~~繁衍~~反演、狄利克雷卷积等工具。这题当然也可以用这些方法推导式子。 最后上代码。 ```cpp #include <iostream> #include <algorithm> #define MAXN 100005 using namespace std; void eular(int n); //线性筛求欧拉函数 int phi[MAXN]; //用来保存欧拉函数的值 int minp[MAXN],prime[MAXN],pr; //质数表,线性筛用 int main(void){ int n,m; long long ans=0; cin >> n >> m; if (n>m) swap(n,m); //n=min(n,m) eular(n); for (int i=1;i<=n;i++) ans+=((long long)(n)/i)*((long long)(m)/i)*phi[i]; ans*=2; //2倍gcd之和 ans-=(long long)(n)*m; //再减去mn cout << ans << endl; return 0; } void eular(int n){ phi[1]=1; int t; for (int i=2;i<=n;i++){ if (!minp[i]){ minp[i]=i; phi[i]=i-1; //phi(p)=p-1,其中p是质数 prime[pr++]=i; } for (int j=0;j<pr && (t=i*prime[j])<=n;j++){ minp[t]=prime[j]; //minp表示最小质因子 if (prime[j]==minp[i]){ phi[t]=prime[j]*phi[i]; break; } //else phi[t]=(prime[j]-1)*phi[i]; } } } ```