數論分塊
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