學習筆記:數論分塊

玖吉發表於2024-09-04

數論分塊

1.0

可以快速計算一些含有除法向下取整的和式(形如 $\sum_{i=1}^n g(i) \left \lfloor \frac{n}{i}\right \rfloor $)。

2.0

引理1

\(\left \lfloor \frac{n}{i} \right \rfloor\) 的取值最多隻有 \(2\times \sqrt n\) 種。

證明

對於 \(i \le \left \lfloor \sqrt n \right \rfloor\),顯然有 \(\sqrt n\) 種取值。

對於 \(i > \sqrt n\),有 \(\left \lfloor \frac{n}{i} \right \rfloor \le \left \lfloor \sqrt n \right \rfloor\),有 \(\sqrt n\) 種取值。

2.1

引理2

使得 \(\left \lfloor \frac{n}{l} \right \rfloor = \left \lfloor \frac{n}{r} \right \rfloor\) 且滿足 \(l \le r \le n\)\(r\) 值最大為 \(\left \lfloor \frac{n}{\left \lfloor \frac{n}{l} \right \rfloor} \right \rfloor\)

證明

\(k = \left \lfloor \frac{n}{i} \right \rfloor\),有 \(\left \lfloor \frac{n}{k} \right \rfloor \ge \left \lfloor \frac{n}{\frac{n}{i}} \right \rfloor = i\)

$ \left \lfloor \frac{n}{\frac{n}{i}} \right \rfloor$ 為滿足條件的最大值。

3.0

UVA11526

題意

多組資料,求 \(\sum_{i=1}^n \left \lfloor \frac{n}{i} \right\rfloor\)

\(T\le1000 ,n\le 2^{31}-1\)

解題

void Work() {
	ll n, res = 0;
	cin >> n;
	for(ll l = 1, r, k; l <= n; l = r+1) {
		k = n/l;
		r = n/k;
		res += k * (r-l+1);
	}
	cout << res << "\n";
}


P3935

題意

\(x\) 分解質因數結果為 \(x=p_1^{k_1}p_2^{k_2}\cdots p_n^{k_n}\),令\(f(x)=(k_1+1)(k_2+1)\cdots (k_n+1)\),求 \(\sum_{i=l}^rf(i)\)\(998\,244\,353\) 取模的結果。

\(1\le l \le 10^{14}\)\(1\le r \le 1.6\times 10^{14}\)\(r-l>10^{14}\)

解題

引理1

\(x\) 分解質因數結果為 \(x=p_1^{k_1}p_2^{k_2}\cdots p_n^{k_n}\),則 \(f(x)\)\(x\) 的因數個數。

證明

對於 \(x\) 的一個因數 \(y\),將其必定能表示為 \(y=p_1^{a_1}p_2^{a_2}\cdots p_n^{a_n}\)

且每個 \(a_i\)\([0, k_i]\)\(k_i+1\) 種取值。

\(x\) 的因數個數為 \((k_1+1)(k_2+1)\cdots (k_n+1)\)

引理2

\(1\)\(n\) 中,含有因子 \(d(1\le d\le n)\) 的數的個數為 \(\left \lfloor \frac{n}{d} \right \rfloor\)

證明略。

所以題目要求的即是 \(\sum_{i=1}^r \left \lfloor \frac{r}{i} \right\rfloor - \sum_{i=1}^{l-1} \left \lfloor \frac{l-1}{i} \right\rfloor\)。套板子即可。


P2261

題意

\(\sum_{i=1}^nk \bmod i,1\le n,k \le 10^9\)

解題

\(\sum_{i=1}^n k \bmod i = \sum_{i=1}^n k - \left \lfloor \frac{k}{i} \right \rfloor \times i = n \times k - \sum_{i=1}^n \left \lfloor \frac{k}{i} \right \rfloor \times i\)

考慮怎麼求出 \(\sum_{i=1}^n \left \lfloor \frac{k}{i} \right \rfloor \times i\)

對於一段區間,其值一定,考慮求 \(\sum_{i=l}^r d\times i\),用等差數列求和公式即可,首項為 \(l\times d\),末項為 \(r\times d\),項數為 \(r-l+1\)

code:


#include<iostream>
#include<cstdio>
#define F(i, l, r) for(int (i) = (l); (i) <= (n); (i) ++)
#define ll long long
using namespace std;
ll n, k;
int main() {
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	cin >> n >> k;
	ll res = 0;
	for(ll l = 1, r, d; l <= n; l = r+1) {
		d = k/l;
		if(!d) break;
		r = min(k/d, n);
      		//注意邊界
		res += (r-l+1) * (l + r) * d / 2;
	}
	cout << n*k - res << "\n";
	return 0;
} 


P2260

題意

\(\sum_{i=1}^n \sum_{j=1}^m(n \bmod i) \times (m \bmod j), i \ne j\)

\((1\le n,m\le10^9)\)

解題

原式 \(= \sum_{i=1}^n(n \bmod i) \times \sum_{j=1}^m(m \bmod j) - \sum_{i=1}^{\min(n,m)}(n\bmod i)\times(m\bmod i)\)

\(= \sum_{i=1}^n(n-\left\lfloor \frac{n}{i} \right\rfloor\times i) \times \sum_{j=1}^m(m-\left\lfloor \frac{m}{i} \right\rfloor\times i) - \sum_{i=1}^{\min(n,m)}(n-\left\lfloor \frac{n}{i}\right\rfloor\times i)\times(m-\left\lfloor\frac{m}{i}\right\rfloor\times i)\)

前面部分同上題,考慮如何求最後一部分。

\(\sum_{i=1}^{\min(n,m)}(n-\left\lfloor \frac{n}{i}\right\rfloor\times i)\times(m-\left\lfloor\frac{m}{i}\right\rfloor\times i)\)

\(=\sum_{i=1}^{\min(m,n)}(nm-n\left\lfloor\frac{m}{i}\right\rfloor\times i - m\left\lfloor\frac{n}{i}\right\rfloor\times i+\left\lfloor\frac{n}{i}\right\rfloor \left\lfloor\frac{m}{i}\right\rfloor\times i^2)\)

發現只有最後一項不好求,我們有:

\(\sum_{i=1}^n i^2 = \frac{n(n+1)(2n+1)}{6}\)

證明

\(\sum_{i=1}^n i^2 = \sum_{i=1}^ni(i-1)+i = \sum_{i=1}^n 2\times\binom{i}{2} +i\)

接著逆用帕斯卡公式:\(\binom{n}{k} = \binom{n-1}{k-1} + \binom{n-1}{k}\)

\(2\times\sum_{i=1}^n \binom{i}{2} = 2\times \binom{n+1}{3} = \frac{n(n+1)(n-1)}{3}\)

代入原式,得

\(\sum_{i=1}^n i^2 = \sum_{i=1}^ni(i-1)+i = \sum_{i=1}^n \binom{i}{2} +i = \frac{n(n+1)(n-1)}{3} + \frac{n(n+1)}{2} = \frac{n(n+1)(2n+1)}{6}\)

code

#include<bits/stdc++.h>
#define F(i, l, r) for(int (i) = (l); (i) <= (n); (i) ++)
#define ll long long
#define i128 __int128
using namespace std;
const ll mo = 19940417;
ll calc(ll n) {
	ll res = n * n;
	for(ll l = 1, r, d; l <= n; l = r+1) {
		d = n/l;
		r = n/d;
		res -= d * (l+r) * (r-l+1) / 2;
	}
	return res % mo;
}
ll funa(ll l, ll r) {
	return ((i128)(l+r) * (r-l+1) / 2 % mo);
}
ll func(ll x) {
	return ((i128)x * (x+1) * (2*x+1) / 6 % mo);
}
ll funb(ll l, ll r) {
	return ((func(r) - func(l-1)) % mo + mo) % mo;
}
int main() {
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	ll n, m;
	cin >> n >> m;
	ll a = calc(n), b = calc(m);
	ll w = min(n, m);
	ll c = n * m % mo * w % mo;
	for(ll l = 1, r, d; l <= w; l = r+1) {
		ll num1 = n/l, num2 = m/l;
		r = min(n/num1, m/num2);
		c = ((c - 
		funa(l, r) * ((m*num1 + n*num2) % mo)%mo
		+ (funb(l, r) * num1 % mo * num2 % mo )%mo) %mo + mo)%mo;
	}
	cout << ((a * b % mo - c) % mo + mo) % mo;
	return 0;
} 


P3636

題意

求在三維座標系中所有滿足 \(a\le x\times y\times z \le b\) 的點 \((x,y,z)\) 到原點的曼哈頓距離的平方和。

資料範圍:\(1\le a,b\le 3\times 10^8\)

解題

為了方便我們將 \(x,y,z\) 三個數均限制在正整數範圍內,三個數乘積為正有兩負一正和全正共 \(\binom{3}{2} + 1 = 4\) 種情況,所以最終答案乘 \(4\) 即可。

容易想到將詢問進行差分,所以我們考慮求 \(\sum_{i=1}^{n}\sum_{j=1}^{\left\lfloor\frac{n}{i}\right\rfloor}\sum_{k=1}^{\left\lfloor\frac{n}{i\times j}\right\rfloor}(i+j+k)^2\) 即可。

\(\begin{aligned}\sum_{i=1}^{n}\sum_{j=1}^{\left\lfloor\frac{n}{i}\right\rfloor}\sum_{k=1}^{\left\lfloor\frac{n}{i\times j}\right\rfloor}(i+j+k)^2 &=\sum_{i=1}^{n}\sum_{j=1}^{\left\lfloor\frac{n}{i}\right\rfloor}\sum_{k=1}^{\left\lfloor\frac{n}{i\times j}\right\rfloor}i^2 + (j+k)^2 + 2ij + 2ik \\&= \sum_{i=1}^n(i^2\sum_{j=1}^{\left\lfloor\frac{n}{i}\right\rfloor}\left\lfloor\frac{n}{i\times j}\right\rfloor + 2i\sum_{j=1}^{\left\lfloor\frac{n}{i}\right\rfloor}\sum_{k=1}^{\left\lfloor\frac{n}{i\times j}\right\rfloor}(j+k)+\sum_{j=1}^{\left\lfloor\frac{n}{i}\right\rfloor}\sum_{k=1}^{\left\lfloor\frac{n}{i\times j}\right\rfloor}(j+k)^2)\end{aligned}\)

容易發現 \(\sum_{j=1}^{\left\lfloor\frac{n}{i}\right\rfloor}\left\lfloor\frac{n}{i\times j}\right\rfloor,\sum_{j=1}^{\left\lfloor\frac{n}{i}\right\rfloor}\sum_{k=1}^{\left\lfloor\frac{n}{i\times j}\right\rfloor}(j+k),\sum_{j=1}^{\left\lfloor\frac{n}{i}\right\rfloor}\sum_{k=1}^{\left\lfloor\frac{n}{i\times j}\right\rfloor}\) 這三部分用數論分塊都是好維護的。

所以我們數論分塊套數論分塊維護即可。

由於時間限制,應儘量減少取模次數。

code:

#include<iostream>
#include<cstdio>
#define ll long long

using namespace std;
const ll mo = 10007;
ll inv2, inv6;
struct node{
	ll ans1, ans2, ans3;
};
ll Pow(ll a, ll b) {
	ll res = 1;
	for(; b; b>>=1) {
		if(b&1) res = res * a % mo;
		a = a * a % mo;
	}
	return res;
}
inline ll funa(ll l, ll r) {
	return (l+r) * (r-l+1) %mo * inv2 %mo;
}
inline ll ready(ll x) {
	return x * (x+1) % mo * (2*x+1) % mo * inv6 % mo;
}
inline ll funb(ll l, ll r) {
	return ((ready(r) - ready(l-1)) % mo + mo)% mo;
}
node work2(ll n) {
	ll ans1 = 0, ans2 = 0, ans3 = 0;
	for(ll l = 1, r, d; l <= n; l = r+1) {
		d = n/l;
		r = n/d;
		ans1 = (ans1 + (r-l+1) * d ) % mo;
		ans2 = (ans2 + funa(l, r) * d + (r-l+1) * funa(1, d)) % mo;
		ans3 = (ans3 + funb(l, r) * d + (r-l+1) * funb(1, d) + 2 * funa(l, r) * funa(1, d)) % mo;
	}
	node u;
	u.ans1 = ans1;
	u.ans2 = ans2;
	u.ans3 = ans3;
	return u;
}
ll work(ll n) {
	ll ans = 0;
	for(ll l = 1, r, d; l <= n; l = r+1) {
		d = n/l;
		r = n/d;
		node u = work2(d);
		ll ans1 = u.ans1, ans2 = u.ans2, ans3 = u.ans3;
		ans = (ans + funb(l, r)*ans1 + funa(l, r)*2ll*ans2 + (r-l+1)*ans3) % mo;
	}
	return ans;
}
int main() {
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	ll a, b;
	cin >> a >> b;
	inv2 = Pow(2, mo-2);
	inv6 = Pow(6, mo-2);
	cout << (work(b) - work(a-1) + mo) * 4ll%mo;
	return 0;
}

參考資料:

https://www.cnblogs.com/rickylin/p/17790471.html

https://oi-wiki.org/math/number-theory/sqrt-decomposition/

https://www.cnblogs.com/mangoworld/p/Number-Theory-Block.html

https://www.luogu.com/article/jcsvb4vh

相關文章