R語言中繪製曼哈頓圖--線段圖
數論分塊
首先我們需要知道數論分塊的用途:它可以快速計算含有除法向下取整的和式。形如\(\sum_{i=1}^{n}f(i)g(\lfloor{\frac{n}{i}}\rfloor)\).為什麼可以快速計算呢,因為那個g的部分我們可以採用分塊的思想快速求解。也就是就\(O(\sqrt{n})\)的時間複雜度裡面求解。
下面講解為什麼是這樣的:
因為\(\lfloor{\frac{n}{i}}\rfloor\)的值是一個塊狀分佈,就是所有的值聚集在一個連續的快裡面。例如:n=21時,我們按照\(\lfloor{\frac{21}{i}}\rfloor\)的值不同,可以把i的值域分為8塊:{1},{2},{3},{4},{5},{6,7},{8,9,10}.{11,12,13,\(\cdots,21\)
這裡我們可以根據影象進行理解:
證明1:分塊的快數需要\(\leq2 \lfloor{\sqrt{n}}\rfloor\)
當我們的\(i\leq\lfloor{\sqrt{n}}\rfloor\),\(\lfloor{\frac{n}{i}}\rfloor\)有\(\lfloor{\sqrt{n}}\rfloor\)種取值。這裡我們可以這麼記憶,也就是i從\(1\sim \sqrt{n}\)有多少種不同的取值。然後下取整一下就好了。
當\(i\ge\sqrt{n}\)時,\(\lfloor{\frac{n}{i}}\rfloor \leq \lfloor{\sqrt{n}},所以\lfloor{\frac{n}{i}}\rfloor最多有根號n種取值。\)
證明2:i所在塊的右端點是\(\lfloor{\frac{n}{\lfloor{\frac{n}{i}}\rfloor}}\rfloor\)
一定要注意這個k是指i所在塊的值,也就是函式的縱座標.
例題講解
[題目連結]([CQOI2007]餘數求和 - 洛谷)
核心思路:首先我們應該對於k mod i 進行化簡。\(k\pmod{i}=k-(k/i)*i\).然後我們會驚奇的發現後面那一部分就是我們剛開始的求和公式。然後我們就可以採用在\(\sqrt{n}\)的時間複雜度求出來\(t=\lfloor{\frac{k}{i}}\rfloor\).
具體的思路是我們可以利用t在這個\([L,R]\)
\(對於\sum_{i=1}^{r}i\)這個求和就是很容易的了。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 110, M = 11, MOD = 1e8;
int main()
{
LL n, k;
cin >> n >> k;
LL res = n * k;
LL l = 1,r;
while (l <= n)
{
if (k / l)
r = min(k / (k / l), n);
else//因為一旦k/l等於0就說明這個l已經很大了.所以k/(k/l)就是一個無窮的數了,但是我們右端點最大都是不可以超過n的
r = n;
res -= (k / l) * (r - l + 1) * (l + r) / 2;
l = r + 1;//迭代更新
}
cout << res << endl;
}