问题引入
求
∑ i = 1 n ⌊ n i ⌋ \sum_{i=1}^n \lfloor\frac{n}{i}\rfloor ∑ i = 1 n ⌊ i n ⌋ ,其中
n n n 为常数。
为了方便我们的研究,我使用绘图软件画出了
f ( x ) = 7 x ( 1 ≤ x ≤ 7 ) f(x) =\frac{7}{x}(1\leq x\leq 7) f ( x ) = x 7 ( 1 ≤ x ≤ 7 ) 的图像,也就是一种反比例函数的图像。
因为求的值是向下取整的,显然函数
f ( x ) f(x) f ( x ) 在
[ 1 , 7 ] [1,7] [ 1 , 7 ] 区间内是单调递减的,我们不妨把
⌊ n i ⌋ \lfloor \frac n i\rfloor ⌊ i n ⌋ 取值相同的段取出来
图像被分割为了
7 7 7 个大块,但取值范围包含整数的只有
4 4 4 个,那么如果我们可以把这些包含整数的块取出来,一次性得出一个块的答案,把整块对答案的贡献加上即可。
实现:
如果要实现整块一起统计,我们需要求出每一块的块头
l l l 和块尾
r r r ,则:
∑ i = 1 n ⌊ n i ⌋ = ∑ ( l , r ) ( r − l + 1 ) ⌊ n l ⌋ \sum_{i=1}^n \lfloor \frac n i \rfloor = \sum _{(l,r)} (r-l+1) \lfloor \frac n l \rfloor ∑ i = 1 n ⌊ i n ⌋ = ∑ ( l , r ) ( r − l + 1 ) ⌊ l n ⌋
给出一个结论: 对于整数
i i i ,其所在块的右端点为
⌊ n ⌊ n i ⌋ ⌋ \lfloor\frac{n}{\lfloor \frac n i \rfloor}\rfloor ⌊ ⌊ i n ⌋ n ⌋ ,在此给出两种证明方式:
代数法
首先我们要证明
⌊ n ⌊ n i ⌋ ⌋ \lfloor\frac{n}{\lfloor \frac n i \rfloor}\rfloor ⌊ ⌊ i n ⌋ n ⌋ 与
i i i 在同一块,也就是:
⌊ n ⌊ n ⌊ n i ⌋ ⌋ ⌋ = ⌊ n i ⌋ \lfloor\frac{n}{\lfloor\frac{n}{\lfloor \frac n i \rfloor}\rfloor}\rfloor= \lfloor\frac{n}{i}\rfloor ⌊ ⌊ ⌊ i n ⌋ n ⌋ n ⌋ = ⌊ i n ⌋
易证:
⌊ x ⌋ ≤ x \lfloor x\rfloor \leq x ⌊ x ⌋ ≤ x
x ≤ y → ⌊ x ⌋ ≤ ⌊ y ⌋ x \le y\to \lfloor x\rfloor \le \lfloor y\rfloor x ≤ y → ⌊ x ⌋ ≤ ⌊ y ⌋
x ≥ y → ⌊ x ⌋ ≥ ⌊ y ⌋ x \ge y\to \lfloor x\rfloor \ge \lfloor y\rfloor x ≥ y → ⌊ x ⌋ ≥ ⌊ y ⌋
则:
⌊ n ⌊ n ⌊ n i ⌋ ⌋ ⌋ ≥ ⌊ n n ⌊ n i ⌋ ⌋ = ⌊ n i ⌋ \lfloor\frac{n}{\lfloor\frac{n}{\lfloor \frac n i \rfloor}\rfloor}\rfloor \ge \lfloor\frac{n}{\frac{n}{\lfloor \frac n i \rfloor}}\rfloor=\lfloor\frac{n}{i}\rfloor ⌊ ⌊ ⌊ i n ⌋ n ⌋ n ⌋ ≥ ⌊ ⌊ i n ⌋ n n ⌋ = ⌊ i n ⌋
⌊ n ⌊ n ⌊ n i ⌋ ⌋ ⌋ ≤ ⌊ n ⌊ n n i ⌋ ⌋ = ⌊ n i ⌋ \lfloor\frac{n}{\lfloor\frac{n}{\lfloor \frac n i \rfloor}\rfloor}\rfloor \le \lfloor\frac{n}{\lfloor\frac{n}{ \frac n i }\rfloor}\rfloor=\lfloor\frac{n}{i}\rfloor ⌊ ⌊ ⌊ i n ⌋ n ⌋ n ⌋ ≤ ⌊ ⌊ i n n ⌋ n ⌋ = ⌊ i n ⌋
所以:
⌊ n ⌊ n ⌊ n i ⌋ ⌋ ⌋ = ⌊ n i ⌋ \lfloor\frac{n}{\lfloor\frac{n}{\lfloor \frac n i \rfloor}\rfloor}\rfloor= \lfloor\frac{n}{i}\rfloor ⌊ ⌊ ⌊ i n ⌋ n ⌋ n ⌋ = ⌊ i n ⌋
我们还要证明:
i ≤ ⌊ n ⌊ n i ⌋ ⌋ i \le \lfloor\frac{n}{\lfloor \frac n i \rfloor}\rfloor i ≤ ⌊ ⌊ i n ⌋ n ⌋
也就是
⌊ n ⌊ n i ⌋ ⌋ \lfloor\frac{n}{\lfloor \frac n i \rfloor}\rfloor ⌊ ⌊ i n ⌋ n ⌋ 是这个块内最大的,即为块的右端点,这个很好证明:
⌊ n ⌊ n i ⌋ ⌋ ≥ ⌊ n n i ⌋ = i \lfloor\frac{n}{\lfloor \frac n i \rfloor}\rfloor\ge \lfloor\frac{n}{ \frac n i }\rfloor=i ⌊ ⌊ i n ⌋ n ⌋ ≥ ⌊ i n n ⌋ = i
这样我们就以代数方式证明了结论,如果看不懂还有几何的。
几何法
n ⌊ n i ⌋ \frac{n}{\lfloor \frac n i \rfloor} ⌊ i n ⌋ n 的意义即为直线
y = ⌊ n i ⌋ y=\lfloor \frac n i \rfloor y = ⌊ i n ⌋ 与直线
y = n x y=\frac n x y = x n 的交点的横坐标,取整后就是这个交点左侧第一个整数横坐标,如图:
l 1 l_1 l 1 为直线
y = ⌊ n i ⌋ y = \lfloor \frac n i \rfloor y = ⌊ i n ⌋ ,
l 2 l_2 l 2 为直线
y = ⌊ n i ⌋ + 1 y=\lfloor \frac n i \rfloor +1 y = ⌊ i n ⌋ + 1 ,我们不妨设
l 1 l1 l 1 与直线
y = n x y=\frac n x y = x n 的交点为
P P P ,
l 2 l_2 l 2 与直线
y = n x y=\frac n x y = x n 的交点为
Q Q Q ,则:
∀ x Q < x ≤ x p , ⌊ n x ⌋ = ⌊ n i ⌋ \forall x_{Q} < x \le x_{p}, \lfloor\frac n x\rfloor = \lfloor\frac n i\rfloor ∀ x Q < x ≤ x p , ⌊ x n ⌋ = ⌊ i n ⌋
那么
x P x_{P} x P 也就是
n ⌊ n i ⌋ \frac{n}{\lfloor \frac n i \rfloor} ⌊ i n ⌋ n 左侧的第一个整点
⌊ n ⌊ n i ⌋ ⌋ \lfloor \frac{n}{\lfloor \frac n i \rfloor}\rfloor ⌊ ⌊ i n ⌋ n ⌋ 即为这些点里的最大整数点。
证毕。
复杂度证明:
当
x ∈ [ 1 , ⌊ n ⌋ ] x \in [1, \lfloor\sqrt n\rfloor] x ∈ [ 1 , ⌊ n ⌋] 这个区间,最多有
⌊ n ⌋ \lfloor\sqrt n\rfloor ⌊ n ⌋ 种取值。
当
x ∈ ( ⌊ n ⌋ , n ] x \in (\lfloor\sqrt n\rfloor,n] x ∈ (⌊ n ⌋ , n ] 这个区间,
⌊ n x ⌋ \lfloor\frac n x\rfloor ⌊ x n ⌋ 显然只能取到
[ 1 , ⌊ n ⌋ ) [1, \lfloor\sqrt n\rfloor) [ 1 , ⌊ n ⌋) 这个区间,最多有
⌊ n ⌋ \lfloor\sqrt n\rfloor ⌊ n ⌋ 种取值。
则至多有
2 ⌊ n ⌋ 2\lfloor\sqrt n\rfloor 2 ⌊ n ⌋ 种取值,复杂度为
O ( n ) O(\sqrt n) O ( n ) 。
代码:
给出整数
n n n ,求
∑ i = 1 n ⌊ n i ⌋ \sum_{i=1}^n \lfloor\frac{n}{i}\rfloor ∑ i = 1 n ⌊ i n ⌋ 。
我们枚举每一段区间,那么当前区间右节点
+ 1 +1 + 1 就是下个区间的左节点:
CPP for (int l = 1 , r; l <= n; l = r + 1 ) {
r = n / (n / l);
ans += (r - l + 1 ) * (n / l)
}
练习题:
一道比较模板的题,显然题目中的
f ( x ) f(x) f ( x ) 就是
x x x 的约数个数,设
g ( x ) = ∑ i = 1 x f ( i ) g(x)=\sum_{i = 1} ^x f(i) g ( x ) = ∑ i = 1 x f ( i ) , 那么有一个常见套路:
∑ i = l r f ( i ) = g ( r ) − g ( l − 1 ) \sum_{i=l}^r f(i)=g(r) - g(l-1) ∑ i = l r f ( i ) = g ( r ) − g ( l − 1 )
g ( x ) = ∑ i = 1 x f ( i ) = ∑ i = 1 x ∑ k = 1 x [ i ∣ k ] = ∑ i = 1 x ⌊ x i ⌋ g(x)=\sum_{i = 1} ^x f(i)=\sum_{i = 1} ^x \sum_{k = 1} ^x [i\mid k]=\sum_{i=1}^x\lfloor\frac x i\rfloor g ( x ) = ∑ i = 1 x f ( i ) = ∑ i = 1 x ∑ k = 1 x [ i ∣ k ] = ∑ i = 1 x ⌊ i x ⌋
大致的意思就是枚举一下
i ∈ [ 1 , x ] i\in[1,x] i ∈ [ 1 , x ] ,看
i i i 在
[ 1 , x ] [1,x] [ 1 , x ] 中是多少个数的约数,然后加起来就好了,这里有个结论:
∑ i = 1 n [ x ∣ i ] = ⌊ n x ⌋ \sum_{i=1}^n[x \mid i] =\lfloor\frac n x\rfloor ∑ i = 1 n [ x ∣ i ] = ⌊ x n ⌋
然后这个
g ( l − 1 ) g(l - 1) g ( l − 1 ) 和
g ( r ) g(r) g ( r ) 分别整除分块推一下就好,复杂度
O ( n ) O(\sqrt n) O ( n )
代码:
CPP #include <stdio.h>
#define ll long long
#define mod 998244353
ll get (ll x) {
ll ans = 0 ;
for (ll l = 1 , r; l <= x; l = r + 1 ) {
r = x / (x / l);
ans = (ans + (r - l + 1 ) * (x / l)) % mod;
}
return ans;
}
int main () {
ll l, r; scanf ("%lld%lld" , &l, &r);
return printf ("%lld" , (get (r) - get (l - 1 ) + mod) % mod), 0 ;
}
简单推下式子:
G ( n , k ) = ∑ i = 1 n k m o d i = ∑ i = 1 n ( k − i ⌊ k i ⌋ ) = n k − ∑ i = 1 n i ⌊ k i ⌋ = n k − ∑ ( l , r ) ( ∑ i = l r i ) ⌊ k l ⌋ = n k − ∑ ( l , r ) ( r − l + 1 ) ( l + r ) 2 ⌊ k l ⌋ \begin{aligned}
G(n,k)&=\sum_{i=1}^n k\bmod i \\
&=\sum_{i=1}^n ( k- i\lfloor\frac k i\rfloor) \\
&=nk - \sum_{i=1}^{n} i\lfloor\frac k i\rfloor \\
&=nk-\sum_{(l,r)} (\sum_{i=l}^r i)\lfloor\frac k l\rfloor \\
&=nk-\sum_{(l,r)} \frac {(r - l + 1)(l + r)} 2 \lfloor\frac k l\rfloor
\end{aligned} G ( n , k ) = i = 1 ∑ n k mod i = i = 1 ∑ n ( k − i ⌊ i k ⌋) = nk − i = 1 ∑ n i ⌊ i k ⌋ = nk − ( l , r ) ∑ ( i = l ∑ r i ) ⌊ l k ⌋ = nk − ( l , r ) ∑ 2 ( r − l + 1 ) ( l + r ) ⌊ l k ⌋
这里代码中有个细节,就是
l > k l>k l > k 时
⌊ k l ⌋ = 0 \lfloor\frac k l\rfloor=0 ⌊ l k ⌋ = 0 ,那么
⌊ k ⌊ k l ⌋ ⌋ = inf \lfloor \frac k{\lfloor\frac k l\rfloor}\rfloor = \inf ⌊ ⌊ l k ⌋ k ⌋ = inf 会爆炸,特判一下即可。
代码:
CPP #include <stdio.h>
#define ll long long
ll min (ll a, ll b) { return a < b ? a : b; }
int main () {
ll n, k, res = 0 ; scanf ("%lld%lld" , &n, &k);
for (ll l = 1 , r; l <= n; l = r + 1 ) {
r = k / l ? min (n, k / (k / l)) : n;
res += (k / l) * (r - l + 1 ) * (l + r) / 2 ;
}
return printf ("%lld" , n * k - res), 0 ;
}
这道题不是那么板了,当
d d d 取
x x x 时,
[ l , r ] [l,r] [ l , r ] 能取到的必要条件是:
l ≤ x p ≤ r l\leq xp\leq r l ≤ x p ≤ r
⌊ l x ⌋ ≤ p ≤ ⌊ r x ⌋ \lfloor\frac l x\rfloor \leq p\leq\lfloor\frac r x\rfloor ⌊ x l ⌋ ≤ p ≤ ⌊ x r ⌋
⌊ l − 1 x ⌋ < p ≤ ⌊ r x ⌋ \lfloor\frac {l-1} x\rfloor < p\leq\lfloor\frac r x\rfloor ⌊ x l − 1 ⌋ < p ≤ ⌊ x r ⌋
⌊ l − 1 x ⌋ < ⌊ r x ⌋ \lfloor\frac {l-1} x\rfloor < \lfloor\frac r x\rfloor ⌊ x l − 1 ⌋ < ⌊ x r ⌋
那么我们只需要求
l , r l,r l , r 能满足的
d d d 即可。
如果暴力枚举一个
d d d 显然会超时,我们考虑二维数论分块,即使每一个块内的数除两个数结果都是一定的。
怎么实现的呢?如果我们求出来对于第一个数
x x x 的右端点是
r 1 r_1 r 1 ,对于第二个数
y y y 的右端点是
r 2 r_2 r 2 ,那么我们
r r r 取
min ( r 1 , r 2 ) \min(r_1,r_2) min ( r 1 , r 2 ) 即可使块内除两个数结果都使一定的。
如果当前块
a a a 满足我们求出来的条件(
⌊ l − 1 x ⌋ < p ≤ ⌊ r x ⌋ \lfloor\frac {l-1} x\rfloor < p\leq\lfloor\frac r x\rfloor ⌊ x l − 1 ⌋ < p ≤ ⌊ x r ⌋ ),那么这一段对
d ∈ [ l a , r a ] d \in [l_a,r_a] d ∈ [ l a , r a ] 都有贡献,我们差分一下即可。
在此处还需加上一个剪枝,当
l < l a ≤ r l < l_a\leq r l < l a ≤ r 时,
⌊ l l a ⌋ = 0 < ⌊ r l a ⌋ \lfloor\frac l {l_a}\rfloor = 0< \lfloor\frac r {l_a}\rfloor ⌊ l a l ⌋ = 0 < ⌊ l a r ⌋ ,这一段一定都满足。
代码:
CPP #include <stdio.h>
#define Maxm 1000005
int min (int a, int b) { return a < b ? a : b; }
int d[Maxm];
int main () {
int n, m, x, y; scanf ("%d%d" , &n, &m);
for (int i = 1 ; i <= n; ++ i) {
scanf ("%d%d" , &x, &y); x --;
for (int l = 1 , r; l <= x; l = r + 1 ) {
r = min (x / (x / l), y / (y / l));
if (((x - 1 ) / l) < (y / l)) d[l] ++, d[r + 1 ] --;
}
d[x + 1 ] ++, d[y + 1 ] --;
}
for (int i = 1 , p = 0 , ans = 0 ; i <= m; ++ i) {
ans += d[i];
printf ("%d\n" , ans);
}
}