可以说是卷积和反演的综合应用
一、整除分块(数论分块)
补充一点基础知识…
具体来说就是:如果我们要求 ∑ i = 1 n ⌊ n i ⌋ \sum \limits_{i=1}^n\lfloor\frac{n}{i}\rfloor i=1∑n⌊in⌋,并不需要用朴素算法 O ( n ) O(n) O(n)去求,可以加快速度。
定理1:所有 1 ≤ i ≤ n 1 \leq i \leq n 1≤i≤n的 ⌊ n i ⌋ \lfloor\frac{n}{i}\rfloor ⌊in⌋的结果最多只有 2 n 2\sqrt n 2n种。
证明:对于 i ∈ [ 1 , n ] i \in [1,\sqrt n] i∈[1,n],显然最多只有 n \sqrt n n种取值,而对于 i ∈ [ n + 1 , n ] i \in [\sqrt n+1,n] i∈[n+1,n], ⌊ n i ⌋ ≤ n \lfloor\frac{n}{i}\rfloor\leq\sqrt n ⌊in⌋≤n,因此也最多有 n \sqrt n n种取值,两个部分相加,因此最多有 2 n 2\sqrt n 2n种取值。
定理2:对于确定的 i i i,能使得 ⌊ n x ⌋ = ⌊ n i ⌋ \lfloor\frac{n}{x}\rfloor=\lfloor\frac{n}{i}\rfloor ⌊xn⌋=⌊in⌋的 x m a x = ⌊ n ⌊ n i ⌋ ⌋ x_{max}=\Big\lfloor\cfrac{n}{\lfloor\frac{n}{i}\rfloor}\Big\rfloor xmax=⌊⌊in⌋n⌋。
这里选取了两种证明方法。
证法1:记 f ( i ) = ⌊ n ⌊ n i ⌋ ⌋ f(i)=\Big\lfloor\cfrac{n}{\lfloor\frac{n}{i}\rfloor}\Big\rfloor f(i)=⌊⌊in⌋n⌋,则因为 ⌊ n i ⌋ \lfloor \frac{n}{i} \rfloor ⌊in⌋单调不减,而且 f ( i ) ≥ i f(i) \ge i f(i)≥i,所以必有 ⌊ n f ( i ) ⌋ ≤ ⌊ n i ⌋ \lfloor\frac{n}{f(i)}\rfloor \leq \lfloor \frac{n}{i} \rfloor ⌊f(i)n⌋≤⌊in⌋,又因为 f ( i ) ≤ n ⌊ n i ⌋ f(i)\leq\cfrac{n}{\lfloor\frac{n}{i}\rfloor} f(i)≤⌊in⌋n,所以有:
⌊ n f ( i ) ⌋ ≥ n / ( n ⌊ n i ⌋ ) = ⌊ n i ⌋ \lfloor\frac{n}{f(i)}\rfloor \ge n/(\frac{n}{\lfloor\frac{n}{i}\rfloor})=\lfloor\frac{n}{i}\rfloor ⌊f(i)n⌋≥n/(⌊in⌋n)=⌊in⌋
同时证明大于等于和小于等于,因此 ⌊ n f ( i ) ⌋ = ⌊ n i ⌋ \lfloor\frac{n}{f(i)}\rfloor=\lfloor\frac{n}{i}\rfloor ⌊f(i)n⌋=⌊in⌋。
另外,若 f ( i ) f(i) f(i)不是满足条件的最大值,则 ⌊ n f ( i ) + 1 ⌋ = ⌊ n i ⌋ \lfloor\frac{n}{f(i)+1}\rfloor=\lfloor\frac{n}{i}\rfloor ⌊f(i)+1n⌋=⌊in⌋,则 f ( i ) = f ( f ( i ) + 1 ) f(i)=f(f(i)+1) f(i)=f(f(i)+1),而又 f ( i ) ≥ i f(i)\ge i f(i)≥i,矛盾,因此 f ( i ) f(i) f(i)就是满足要求的最大值。
证法2:记 n = d i + p = d x + q ( 0 ≤ p , q < n ) n=di+p=dx+q\quad(0\leq p,q<n) n=di+p=dx+q(0≤p,q<n),则 ⌊ n x ⌋ = ⌊ n i ⌋ = d \lfloor\frac{n}{x}\rfloor=\lfloor\frac{n}{i}\rfloor=d ⌊xn⌋=⌊in⌋=d,由前式两等式相减得:
q = p − d ( x − i ) q = p − d k ( k = x − i ) q=p-d(x-i)\qquad\qquad q=p-dk\quad(k=x-i) q=p−d(x−i)q=p−dk(k=x−i)
要使 x x x最大,也就是要让 k k k尽量大,由 q ≥ 0 q\ge 0 q≥0可得 k k k的最大值为 k m a x = ⌊ p d ⌋ k_{max}=\lfloor\frac{p}{d}\rfloor kmax=⌊dp⌋。
此时有: x = i + d m a x = i + ⌊ p d ⌋ x=i+d_{max}=i+\lfloor\frac{p}{d}\rfloor x=i+dmax=i+⌊dp⌋,将 p , d p,d p,d代换得: x = i + ⌊ n m o d i ⌊ n i ⌋ ⌋ x=i+\Big\lfloor\frac{n\ mod\ i}{\lfloor\frac{n}{i}\rfloor}\Big\rfloor x=i+⌊⌊in⌋n mod i⌋,将 n m o d i n\ mod\ i n mod i代换,并将 i i i乘上去可得:
x = i + ⌊ n − i ⌊ n i ⌋ ⌊ n i ⌋ ⌋ = ⌊ i ⌊ n i ⌋ + n − i ⌊ n i ⌋ ⌊ n i ⌋ ⌋ = ⌊ n ⌊ n i ⌋ ⌋ x=i+\Big\lfloor\frac{n-i\lfloor\frac{n}{i}\rfloor}{\lfloor\frac{n}{i}\rfloor}\Big\rfloor=\Big\lfloor\frac{i\lfloor\frac{n}{i}\rfloor+n-i\lfloor\frac{n}{i}\rfloor}{\lfloor\frac{n}{i}\rfloor}\Big\rfloor=\Big\lfloor\cfrac{n}{\lfloor\frac{n}{i}\rfloor}\Big\rfloor x=i+⌊⌊in⌋n−i⌊in⌋⌋=⌊⌊in⌋i⌊in⌋+n−i⌊in⌋⌋=⌊⌊in⌋n⌋
同样得到了结论。
因此我们只要通过上面对商的讨论,就可以在 O ( n ) O(\sqrt n) O(n)时间复杂度内快速计算 ∑ i = 1 n ⌊ n i ⌋ \sum \limits_{i=1}^n\lfloor\frac{n}{i}\rfloor i=1∑n⌊in⌋。
for (int l=1,r;l<=n;l=r+1) {
r=n/(n/l);
ans+=(n/l)*(r-l+1);
}
来看一道题目。
题目大意:给定 n , k ≤ 1 0 9 n,k \leq 10^9 n,k≤109,求出 ∑ i = 1 n k m o d i = k m o d 1 + . . . + k m o d n \sum \limits_{i=1}^nk\ mod\ i=k\ mod\ 1+...+k\ mod\ n i=1∑nk mod i=k mod 1+...+k mod n。洛谷2261
分析:直接 O ( n ) O(n) O(n)做是肯定不行的,这时我们想到利用余数的定义: n m o d i = n − i ⌊ n i ⌋ n\ mod\ i=n-i\lfloor\frac{n}{i}\rfloor n mod i=n−i⌊in⌋,于是我们就可以开心化简了:
∑ i = 1 n k m o d i = k n − ∑ i = 1 n i ⌊ k i ⌋ \sum \limits_{i=1}^n k\ mod\ i=kn-\sum\limits_{i=1}^n i\lfloor\frac{k}{i}\rfloor i=1∑nk mod i=kn−i=1∑ni⌊ik⌋
而这个式子的后面一部分非常像整除分块的形式,但是还不完全一样。不过这也不难,我们只要将上面的代码中原本表示 l − r l-r l−r之间数的个数的 ( r − l + 1 ) (r-l+1) (r−l+1)替换成 l − r l-r l−r的和 ( l + r ) ( r − l + 1 ) 2 \frac{(l+r)(r-l+1)}{2} 2(l+r)(r−l+1)即可。
有一点需要注意,此时求和上限和被除数不同,需要加上一句额外判断,当 ⌊ k ⌊ k i ⌋ ⌋ \Big\lfloor\cfrac{k}{\lfloor\frac{k}{i}\rfloor}\Big\rfloor ⌊⌊ik⌋k⌋超过了 n n n时也只能取到 n n n。
#include <bits/stdc++.h>
using namespace std;
long long ans,n,k;
int main () {
scanf("%lld%lld",&n,&k);
ans=n*k;
for (long long l=1,r;l<=min(n,k);l=r+1) {
r=min(k/(k/l),n);
ans-=(k/l)*(l+r)*(r-l+1)/2;
}
printf("%lld\n",ans);
return 0;
}
通过上面这道题目我们能看出来什么?其实整除分块并不只能求原本的标准形式。事实上,形如整除式后面乘上一个函数的值这样的求和式,都可以用整除分块的方法做出,即:
∑ i = 1 n ⌊ n i ⌋ f ( i ) = ∑ ⌊ n l ⌋ ( s ( r ) − s ( l − 1 ) ) \sum \limits_{i=1}^n\ \lfloor\frac{n}{i}\rfloor\ f(i)=\sum \ \lfloor\frac{n}{l}\rfloor\ (s(r)-s(l-1)) i=1∑n ⌊in⌋ f(i)=∑ ⌊ln⌋ (s(r)−s(l−1))
其中 s ( k ) = ∑ i = 1 k f ( i ) s(k)=\sum \limits_{i=1}^kf(i) s(k)=i=1∑kf(i),即函数的前缀和, l , r l,r l,r与上面代码中的意义相同。其实上面的例子也是这样,只是 s ( i ) s(i) s(i)恰好是一个等差数列的形式,很容易算出。
二、gcd快速求和
由于这个部分比较好玩,而且与反演和卷积也有点关系,体现一些基本思想,就先讲一些了。
一维gcd求和:求 ∑ i = 1 N g c d ( N , i ) \sum\limits_{i=1}^Ngcd(N,i) i=1∑Ngcd(N,i)。洛谷2303
分析:我们对不同的数据范围进行逐层递进的分析:
(1) N ≤ 1 0 5 N \leq 10^5 N≤105
这个非常简单,只要你会欧几里得算法,就可以直接计算 g c d ( N , i ) gcd(N,i) gcd(N,i)的值,然后将所有结果相加,暴力计算即可得到答案,时间复杂度 O ( N log N ) O(N\log N) O(NlogN);
(2) N ≤ 1 0 9 N \leq 10^9 N≤109
主要的思维都体现在这里了…首先处理这种最大公因数问题的最常用方法之一,是枚举最大公因数的值 d d d,也就是我们枚举每一个可能的 d d d,然后去计数有多少个为 d d d的结果,将 d d d与计数量相乘即可得到结果为 d d d的最大公因数和了,而 d = g c d ( N , i ) d=gcd(N,i) d=gcd(N,i),则显然 d ∣ N d|N d∣N,因此可以这样枚举:
∑ d ∣ N d ∑ i = 1 N [ g c d ( N , i ) = d ] \sum \limits_{d|N}\ d\ \sum\limits_{i=1}^N[gcd(N,i)=d] d∣N∑ d i=1∑N[gcd(N,i)=d]
这是第一步技巧,将gcd提取出来放在外面。
接下来还要继续化简,我们现在感觉这个方括号里的等式真的没什么用,那么遇到这种情况有两种方法,一种是用后面要讲的莫比乌斯反演,但这里还不必要;另一种就是“约分”,也就是将内侧求和的每一个d都约去,这样的话 N N N就变成了 N d \frac{N}{d} dN了:
∑ d ∣ N d ∑ i = 1 N d [ g c d ( N d , i ) = 1 ] \sum \limits_{d|N}\ d\ \sum\limits_{i=1}^\frac{N}{d}[gcd(\frac{N}{d},i)=1] d∣N∑ d i=1∑dN[gcd(dN,i)=1]
这时候你就应该意识到了:此时里面这个求和号其实就是欧拉函数!我们将它表示一下:
∑ d ∣ N d φ ( N d ) \sum \limits_{d|N}\ d\ \varphi(\frac{N}{d}) d∣N∑ d φ(dN)
这样做 1 0 9 10^9 109数据就足够了,我们枚举N的因子d,并算出N/d的欧拉函数值就可以了。
(3) N ≤ 1 0 12 N \leq 10^{12} N≤1012
这时上面计算欧拉函数的时间代价就太高了,因此需要再次优化。
如果你掌握之前讲的卷积足够好的话,你就应该及时意识到,上面的这个式子是一个卷积式,其实是 i d id id与 φ \varphi φ的卷积。之前我们还学过:两个积性函数的卷积依然是积性函数,所以此时我们假设 f ( N ) = ∑ d ∣ N d φ ( N d ) f(N)=\sum \limits_{d|N}\ d\ \varphi(\frac{N}{d}) f(N)=d∣N∑ d φ(dN),此时 f f f就是一个积性函数。那么我们将 n n n分解素因数为 N = p 1 c 1 . . . p n c n N=p_1^{c_1}...p_n^{c_n} N=p1c1...pncn,则: