1. 程式人生 > >【舊部落格搬運】整除分塊學習筆記

【舊部落格搬運】整除分塊學習筆記

整除分塊學習筆記

Luogu P2261 [CQOI2007]餘數求和

題目:

給定\(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}\]