【舊部落格搬運】整除分塊學習筆記
整除分塊學習筆記
題目:
給定\(n, k\)(\(n,k\leq 10^9\)),求\[\sum_{i=1}^n i\rm \;mod \;k\]
分析:
\[\sum_{i=1}^n i\rm \;mod \;k\]
由取模意義可得:
\[=\sum_{i=1}^n k-i\times \left\lfloor\frac{k}{i}\right\rfloor\]
\[=nk-\sum_{i=1}^n i\times \left\lfloor\frac{k}{i}\right\rfloor\]
分析樣例:
\(i\) |
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
\(\left\lfloor\frac{k}{i}\right\rfloor\) | 5 | 2 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
發現\(\left\lfloor\frac{k}{i}\right\rfloor\)取值很少(在\(\sqrt{k}\)左右),對於一個區間\([l,r]\),滿足
\[\forall x,y\in[l,r]\quad\left\lfloor\frac{k}{x}\right\rfloor=\left\lfloor\frac{k}{y}\right\rfloor\]
那麼,若已知區間左端點\(l\),如何求出\(r\)?
設\(p=\left\lfloor\frac{k}{l}\right\rfloor\),即所有\(x\in[l,r]\)的\(\left\lfloor\frac{k}{x}\right\rfloor\)的值
要求區間的右端點,即使\(x\)最大並且滿足:
\[\left\lfloor\frac{k}{x}\right\rfloor=p\]
由向下取整定義可得
\[\frac{k}{x}\geq p\]
\[\because x>0\]
\[\therefore k\geq px\]
\[x\leq \frac{k}{p}\]
由\(x\)取最大正整數(區間右端點)可得:
\[x=\left\lfloor\frac{k}{p}\right\rfloor\]
所以區間右端點:\[r=\left\lfloor\frac{k}{\left\lfloor\frac{k}{l}\right\rfloor}\right\rfloor\]
回到開始的式子,我們已經求出了區間,對於區間內的答案,為(此處省略\(nk\),因為可以一開始就算好):
\[\sum_{i=l}^r i\times \left\lfloor\frac{k}{i}\right\rfloor\]
因為區間內的\(\left\lfloor\frac{k}{i}\right\rfloor\)相同,所以可以直接取\(\left\lfloor\frac{k}{l}\right\rfloor\)
\[\sum_{i=l}^r i\times \left\lfloor\frac{k}{l}\right\rfloor\]
\[=\frac{(l+r)(r-l+1)}{2}\times\left\lfloor\frac{k}{l}\right\rfloor\]
最後的答案即為\(nk\)減去所有區間答案的和
(注意:若\(\left\lfloor\frac{k}{l}\right\rfloor=0\),則區間為\([l,\infty]\),實現時設定成\(n\)即可,運算中出現\(r>n\)也需要設定\(r=n\))
若還有不理解,參考程式碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll ans = 0, l = 1, r = 0, n, k;
int main()
{
cin >> n >> k;
while(r < n)
{
r = (k / l ? min(k / (k / l), n) : n);
ans += (l + r) * (r - l + 1) / 2 * (k / l);
l = r + 1;
}
cout << n * k - ans << endl;
return 0;
}
後記:
發現沒有多少個題解(也有可能是我沒看到)完整的解釋了右邊界\(r\)的來歷,於是花了一個下午(沒辦法我太弱了\(qwq\))來寫出完整過程,過程會有點囉嗦,\(\rm dalao\)不要介意。
\[\frak {The\;End}\]