【数学】数论分块(整除分块)

Description

数论分块,通常用于快速求解形如 ∑ i = 1 n f ( i ) ⋅ g ( ⌊ n i ⌋ ) \sum\limits_{i=1}^n f(i) \cdot g\left(\left\lfloor\frac{n}{i}\right\rfloor\right) i=1nf(i)g(in) 的和式,所以通常被称为 整除分块,当能用 O ( 1 ) O(1) O(1) 计算出 ∑ i = l r f ( i ) \sum\limits_{i=l}^rf(i) i=lrf(i) 时,数论分块便能用 O ( n ) O(\sqrt n) O(n ) 的时间计算出上式的值、数论分块经常搭配 莫比乌斯反演 一起使用。

Conclusion

∀ n , l ∈ N ∗ , l ≤ n \forall n,l\in \mathbb{N}^*,l\le n n,lN,ln,使得
⌊ n l ⌋ = ⌊ n r ⌋ \left\lfloor\dfrac{n}{l}\right\rfloor=\left\lfloor\dfrac{n}{r}\right\rfloor ln=rn
成立的最大的满足 l ≤ r ≤ n l\le r\le n lrn r r r 的值为 ⌊ n ⌊ n l ⌋ ⌋ \left\lfloor\dfrac{n}{\left\lfloor\frac{n}{l}\right\rfloor}\right\rfloor lnn

也就是说,若某个块内所有数的值为 ⌊ n l ⌋ = k \left\lfloor\dfrac{n}{l}\right\rfloor = k ln=k,那么这个块的右端点就是 r = ⌊ n k ⌋ r=\left\lfloor\dfrac{n}{k}\right\rfloor r=kn

Proof

对于这个块中的任意一个数 x x x,应当满足 ⌊ n x ⌋ = k \left\lfloor\dfrac{n}{x}\right\rfloor = k xn=k,即 n = x k + r ( 0 ≤ r < x ) n=xk+r(0\le r < x) n=xk+r(0r<x)

n , k n,k n,k 已知时,只要确定 x x x,就有一个 r r r 与之对应。
n = x k + r n ≥ x k x ≤ n k n = xk + r \\ n \ge xk \\ x \le \dfrac{n}{k} n=xk+rnxkxkn
x x x 的最大值为 ⌊ n k ⌋ \left\lfloor\dfrac{n}{k}\right\rfloor kn

r = ⌊ n ⌊ n l ⌋ ⌋ r = \left\lfloor\dfrac{n}{\left\lfloor\frac{n}{l}\right\rfloor}\right\rfloor r=lnn

证毕。

Method

一个块内的所有数都相等,所以每块每块地求和即可。

在找到上一个块的右端点后,加一就可以得到下一个块的左端点。

UVA11526 H(n)

模板题,求 ∑ i = 1 n ⌊ n i ⌋ \sum\limits_{i=1}^n \left\lfloor\dfrac{n}{i}\right\rfloor i=1nin

//18 = 9 + 9 = 18.
#include <iostream>
#include <cstdio>
#define Debug(x) cout << #x << "=" << x << endl
#define int long long
using namespace std;

int H(int n)
{
	int res = 0;
	for (int l = 1, r; l <= n; l = r + 1)
	{
		int k = n / l;
		r = n / k;
		res += k * (r - l + 1);
	}
	return res;
}

signed main()
{
	int t;
	scanf("%lld", &t);
	while (t--)
	{
		int n;
		scanf("%lld", &n);
		printf("%lld\n", H(n));
	}
	return 0;
}

Complexity

Lemma

∀ n , i ∈ N ∗ , i ≤ n \forall n,i \in \mathbb{N}^*,i\le n n,iN,in ⌊ n i ⌋ \left\lfloor\dfrac{n}{i}\right\rfloor in 的不同值最多有 2 ⌊ n ⌋ 2\left\lfloor\sqrt{n}\right\rfloor 2n 个,即最多有 2 ⌊ n ⌋ 2\left\lfloor\sqrt{n}\right\rfloor 2n 个块。

Proof

∀   i ≤ ⌊ n ⌋ \forall \, i\le \left\lfloor\sqrt{n}\right\rfloor in i i i 只有 ⌊ n ⌋ \left\lfloor\sqrt{n}\right\rfloor n 种取值,则 ⌊ n i ⌋ \left\lfloor\dfrac{n}{i}\right\rfloor in 只有至多 ⌊ n ⌋ \left\lfloor\sqrt{n}\right\rfloor n 种取值;

∀   i > ⌊ n ⌋ \forall \, i > \left\lfloor\sqrt{n}\right\rfloor i>n ,有 i ≥ ⌊ n ⌋ + 1 > n i \ge \left\lfloor\sqrt{n}\right\rfloor + 1 > \sqrt{n} in +1>n ⌊ n i ⌋ ≤ ⌊ n n ⌋ = ⌊ n ⌋ \left\lfloor\dfrac{n}{i}\right\rfloor \le \left\lfloor\dfrac{n}{\sqrt{n}}\right\rfloor = \left\lfloor\sqrt{n}\right\rfloor inn n=n ,也只有至多 ⌊ n ⌋ \left\lfloor\sqrt{n}\right\rfloor n 种取值。

所以最多只有 2 ⌊ n ⌋ 2\left\lfloor\sqrt{n}\right\rfloor 2n 种取值。

证毕。


由引理可知最多有 2 ⌊ n ⌋ 2\left\lfloor\sqrt{n}\right\rfloor 2n 个块,即 for ⁡ \operatorname{for} for 循环最多会执行 2 ⌊ n ⌋ 2\left\lfloor\sqrt{n}\right\rfloor 2n 次,所以时间复杂度为 O ( n ) O(\sqrt{n}) O(n )

Extension

N N N 维数论分块

求形如 ∑ i = 1 n ⌊ a 1 i ⌋ ⌊ a 2 i ⌋ ⋯ ⌊ a m i ⌋ \sum\limits_{i=1}^n \left\lfloor\dfrac{a_1}{i}\right\rfloor\left\lfloor\dfrac{a_2}{i}\right\rfloor \cdots \left\lfloor\dfrac{a_m}{i}\right\rfloor i=1nia1ia2iam 的和式的值。

r = min ⁡ i = 1 m { ⌊ a i ⌊ a i l ⌋ ⌋ } r=\min\limits_{i=1}^m\left\{\left\lfloor\dfrac{a_i}{\left\lfloor\frac{a_i}{l}\right\rfloor}\right\rfloor\right\} r=i=1minm{laiai} 即可,即对于每一个块的右端点取最小(最接近左端点)的那个作为整体的右端点。

较常用的是 2 2 2 维数论分块。求 ∑ i = 1 n ⌊ n i ⌋ ⌊ m i ⌋ \sum\limits_{i=1}^n \left\lfloor\dfrac{n}{i}\right\rfloor \left\lfloor\dfrac{m}{i}\right\rfloor i=1ninim r = min ⁡ ( ⌊ n ⌊ n l ⌋ ⌋ , ⌊ m ⌊ m l ⌋ ⌋ ) r=\min\left(\left\lfloor\dfrac{n}{\left\lfloor\frac{n}{l}\right\rfloor}\right\rfloor, \left\lfloor\dfrac{m}{\left\lfloor\frac{m}{l}\right\rfloor}\right\rfloor\right) r=min(lnn,lmm),也就是在代码中 r = min(n / (n / l), m / (m / l));

Problems

A

P2261 [CQOI2007]余数求和

Description

计算 ∑ i = 1 n k   m o d   i \sum\limits_{i=1}^n k\bmod i i=1nkmodi

Solution

∑ i = 1 n k   m o d   i = ∑ i = 1 n k − ⌊ k i ⌋ i = n k − ∑ i = 1 n ⌊ k i ⌋ i \begin{aligned} \sum\limits_{i=1}^n k\bmod i & = \sum\limits_{i=1}^n k - \left\lfloor\frac{k}{i}\right\rfloor i \\ & = nk - \sum\limits_{i=1}^n \left\lfloor\frac{k}{i}\right\rfloor i \\ \end{aligned} i=1nkmodi=i=1nkiki=nki=1niki

观察后面一项,对于左端点为 l l l,右端点为 r r r 的块,贡献就是

∑ i = l r ⌊ k i ⌋ i = ∑ i = l r ⌊ k l ⌋ i = ⌊ k l ⌋ ∑ i = 1 r i = ⌊ k l ⌋ ( l + r ) ( r − l + 1 ) 2 \begin{aligned} \sum\limits_{i=l}^r \left\lfloor\dfrac{k}{i}\right\rfloor i & = \sum\limits_{i=l}^r \left\lfloor\dfrac{k}{l}\right\rfloor i \\ & = \left\lfloor\dfrac{k}{l}\right\rfloor \sum\limits_{i=1}^r i \\ & = \left\lfloor\dfrac{k}{l}\right\rfloor \dfrac{(l+r)(r-l+1)}{2} \end{aligned} i=lriki=i=lrlki=lki=1ri=lk2(l+r)(rl+1)
但是这次 i i i 要循环到 n n n 而非 k k k,当 n > k n > k n>k ⌊ k l ⌋ \left\lfloor\dfrac{k}{l}\right\rfloor lk 有可能为 0 0 0,这样 r = ⌊ k ⌊ k l ⌋ ⌋ r = \left\lfloor\dfrac{k}{\left\lfloor\dfrac{k}{l}\right\rfloor}\right\rfloor r=lkk 就无意义了。

发现当 i > k i > k i>k ⌊ k i ⌋ i = 0 \left\lfloor\dfrac{k}{i}\right\rfloor i = 0 iki=0,所以 i i i 循环到 min ⁡ ( n , k ) \min(n,k) min(n,k) 即可。

在取右端点时 r = min ⁡ ( n , ⌊ k ⌊ k l ⌋ ⌋ ) r = \min\left(n, \left\lfloor\dfrac{k}{\left\lfloor\dfrac{k}{l}\right\rfloor}\right\rfloor\right) r=minn,lkk

Code
//18 = 9 + 9 = 18.
#include <iostream>
#include <cstdio>
#define Debug(x) cout << #x << "=" << x << endl
typedef long long ll;
using namespace std;

ll block(int n, int k)
{
	ll res = 0;
	for (int l = 1, r; l <= min(n, k); l = r + 1)
	{
		r = min(n, k / (k / l));
		res += (ll)(k / l) * ((ll)(l + r) * (r - l + 1) >> 1);
	}
	return res;
}

int main()
{
	int n, k;
	scanf("%d%d", &n, &k);
	printf("%lld\n", (ll)n * k - block(n, k));
	return 0;
}

B

P2260 [清华集训2012]模积和

Description


[ ∑ i = 1 n ∑ j = 1 m ( n   m o d   i ) ( m   m o d   j ) , i ≠ j ]   m o d   19940417 \left[\sum\limits_{i=1}^n \sum\limits_{j=1}^m (n \bmod i)(m \bmod j), i \ne j\right]\bmod 19940417 [i=1nj=1m(nmodi)(mmodj),i=j]mod19940417

Solution

假设 n ≤ m n \le m nm(否则交换)。
a n s = ∑ i = 1 n ∑ j = 1 m ( n   m o d   i ) ( m   m o d   j ) − ∑ i = 1 n ( n   m o d   i ) ( m   m o d   i ) = ∑ i = 1 n n   m o d   i ⋅ ∑ j = 1 m m   m o d   j − ∑ i = 1 n ( n   m o d   i ) ( m   m o d   i ) \begin{aligned} ans & = \sum\limits_{i=1}^n \sum\limits_{j=1}^m (n\bmod i)(m\bmod j) - \sum\limits_{i=1}^n (n\bmod i)(m\bmod i) \\ & = \sum\limits_{i=1}^n n\bmod i \cdot \sum\limits_{j=1}^m m\bmod j - \sum\limits_{i=1}^n (n\bmod i)(m \bmod i) \end{aligned} ans=i=1nj=1m(nmodi)(mmodj)i=1n(nmodi)(mmodi)=i=1nnmodij=1mmmodji=1n(nmodi)(mmodi)
前面两项都形如 ∑ i = 1 k k   m o d   i \sum\limits_{i=1}^k k\bmod i i=1kkmodi,用上一题的思路求解。
∑ i = 1 n ( n   m o d   i ) ( m   m o d   i ) = ∑ i = 1 n ( n − ⌊ n i ⌋ i ) ( m − ⌊ m i ⌋ i ) = ∑ i = 1 n ( n m − ⌊ n i ⌋ i ⋅ m − ⌊ m i ⌋ i ⋅ n + ⌊ n i ⌋ ⌊ m i ⌋ i 2 ) = n 2 m − m ∑ i = 1 n ⌊ n i ⌋ i − n ∑ i = 1 n ⌊ m i ⌋ i + ∑ i = 1 n ⌊ n i ⌋ ⌊ m i ⌋ i 2 \begin{aligned} \sum\limits_{i=1}^n (n\bmod i)(m\bmod i) & = \sum\limits_{i=1}^n (n - \left\lfloor\dfrac{n}{i}\right\rfloor i)(m - \left\lfloor\dfrac{m}{i}\right\rfloor i) \\ & = \sum\limits_{i=1}^n (nm - \left\lfloor\dfrac{n}{i}\right\rfloor i\cdot m - \left\lfloor\dfrac{m}{i}\right\rfloor i\cdot n + \left\lfloor\dfrac{n}{i}\right\rfloor \left\lfloor\dfrac{m}{i}\right\rfloor i ^ 2) \\ & = n ^ 2 m - m\sum\limits_{i=1}^n \left\lfloor\dfrac{n}{i}\right\rfloor i - n \sum\limits_{i=1}^n \left\lfloor\dfrac{m}{i}\right\rfloor i + \sum\limits_{i=1}^n \left\lfloor\dfrac{n}{i}\right\rfloor \left\lfloor\dfrac{m}{i}\right\rfloor i ^ 2 \end{aligned} i=1n(nmodi)(mmodi)=i=1n(nini)(mimi)=i=1n(nminimimin+inimi2)=n2mmi=1ninini=1nimi+i=1ninimi2
2 , 3 2,3 2,3 项都是模板,第 4 4 4 项就是一个扩展版中的 2 2 2 维数论分块,对于左端点为 l l l,右端点为 r r r 的块,贡献就是
∑ i = l r ⌊ n i ⌋ ⌊ m i ⌋ i 2 = ⌊ n l ⌋ ⌊ m l ⌋ ∑ i = l r i 2 = ⌊ n l ⌋ ⌊ m l ⌋ ( ∑ i = 1 r i 2 − ∑ i = 1 l − 1 i 2 ) \begin{aligned} \sum\limits_{i=l}^r \left\lfloor\dfrac{n}{i}\right\rfloor \left\lfloor\dfrac{m}{i}\right\rfloor i ^ 2 & = \left\lfloor\dfrac{n}{l}\right\rfloor \left\lfloor\dfrac{m}{l}\right\rfloor \sum\limits_{i=l}^r i ^ 2 \\ & = \left\lfloor\dfrac{n}{l}\right\rfloor \left\lfloor\dfrac{m}{l}\right\rfloor (\sum\limits_{i=1}^r i ^ 2 - \sum\limits_{i=1}^{l-1} i ^ 2) \\ \end{aligned} i=lrinimi2=lnlmi=lri2=lnlm(i=1ri2i=1l1i2)
平方和有公式 ∑ i = 1 n i 2 = n ( n + 1 ) ( 2 n + 1 ) 6 \sum\limits_{i=1}^n i ^ 2 = \dfrac{n(n+1)(2n+1)}{6} i=1ni2=6n(n+1)(2n+1)

Code

函数 block1 ⁡ ( n , m ) \operatorname{block1}(n,m) block1(n,m) 求的是 ∑ i = 1 n ⌊ m i ⌋ i \sum\limits_{i=1}^n \left\lfloor\dfrac{m}{i}\right\rfloor i i=1nimi(其中保证 n ≤ m n \le m nm)。

函数 block2 ⁡ ( n , m ) \operatorname{block2}(n,m) block2(n,m) 求的是 ∑ i = 1 n ⌊ n i ⌋ ⌊ m i ⌋ i 2 \sum\limits_{i=1}^n \left\lfloor\dfrac{n}{i}\right\rfloor \left\lfloor\dfrac{m}{i}\right\rfloor i ^ 2 i=1ninimi2

//18 = 9 + 9 = 18.
#include <iostream>
#include <cstdio>
#define Debug(x) cout << #x << "=" << x << endl
#define int long long
using namespace std;

const int MOD = 19940417;

int x, y;

void exgcd(int a, int b)
{
	if (!b)
	{
		x = 1, y = 0;
		return;
	}
	exgcd(b, a % b);
	int tmp = x;
	x = y;
	y = tmp - a / b * y;
}

int inv(int a)
{
	exgcd(a, MOD);
	x = (x % MOD + MOD) % MOD;
	return x;
}

int block1(int n, int m)
{
	int res = 0;
	for (int l = 1, r; l <= n; l = r + 1)
	{
		r = min(n, m / (m / l));
		res = (res + (m / l) * ((l + r) * (r - l + 1) / 2 % MOD) % MOD) % MOD;
	}
	return res;
}

int part1(int n, int m)
{
	return ((n * n % MOD - block1(n, n)) * (m * m % MOD - block1(m, m)) % MOD + MOD) % MOD;
}

int sum(int n)
{
	return n * (n + 1) % MOD * (2 * n + 1) % MOD * inv(6) % MOD;
}

int block2(int n, int m)
{
	int res = 0;
	for (int l = 1, r; l <= n; l = r + 1)
	{
		r = min(n / (n / l), m / (m / l));
		res = (res + (n / l) * (m / l) % MOD * (sum(r) - sum(l - 1)) % MOD) % MOD;
	}
	return res;
}

int part2(int n, int m)
{
	int a = n * n % MOD * m % MOD, b = block1(n, n) * m % MOD, c = block1(n, m) * n % MOD, d = block2(n, m);
	return ((a - b - c + d) % MOD + MOD) % MOD;
}

signed main()
{
	int n, m;
	scanf("%lld%lld", &n, &m);
	if (n > m)
	{
		swap(n, m);
	}
	printf("%lld\n", ((part1(n, m) - part2(n, m)) % MOD + MOD) % MOD);
	return 0;
}

Reference

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值