1. 程式人生 > 其它 >數論分塊

數論分塊

數論分塊

定義:

數論分塊可以在 \(O \sqrt{n}\) 的時間裡計算一些有除法下取整的和式。

主要是 \(\frac{n}{d}\) 相同的數一起同時計算

定理:

定理 \(1\):

\[a,b,c \in \mathbb{Z}, \lfloor \frac{a}{bc} \rfloor =\lfloor \frac{\lfloor\frac{a}{b}\rfloor}{c}\rfloor \]

證明:

\[\frac{a}{b}=\lfloor \frac{a}{b} \rfloor+r \]

帶入將該值代入原式中即可。

定理 \(2\):

\[|\lfloor \frac{n}{d} \rfloor| \leq \lfloor 2\sqrt{n} \rfloor \]

其中,\(n \leq d\)

\(n,d\) 為正整數 , \(|V|\) 表示集合 \(V\) 的元素個數。

證明:

對於 \(d \leq/\geq \lfloor \sqrt{n} \rfloor\) ,對應式子都只有 \(\lfloor \sqrt{n} \rfloor\) 種取值。

過程:

首先,為了好寫部落格,定義 \(| x |=\lfloor x \rfloor\) (小懶不算懶)

考慮含有 \(|\frac{n}{i}|\) 的求和式子。

對於任意一個 \(i \leq n\) 我們需要找到一個最大的 \(j\),使得 \(|\frac{n}{i}|=|\frac{n}{j}|\)

此時 \(j=|\frac{n}{|\frac{n}{i}|}|\)

顯然 \(i\leq j\leq n\) .

我們設 \(k=|\frac{n}{i}|\) ,證明: 當 \(|\frac{n}{j}|=k\) 時, \(j\) 的最大值為 \(|\frac{n}{k}|\)

\[k \leq \frac{n}{j} < k+1 \]

兩邊同時取倒數,再乘上 \(n\),易得:

\[\frac{n}{k+1}<j \leq \frac{n}{k} \]

因為 \(j \in \mathbb{Z}\) ,所以 \(j_{max}=|\frac{n}{k}|\)

利用上述結論,我們每次以 \([i,j]\) 為一塊,分塊求和即可。

例題:

P2261 [CQOI2007]餘數求和

題意:

計算 \(ans=\sum_{i=1}^n (k \mod i)\)

分析:

顯然,這個式子可以轉化成:

\[n \times k-\sum_{i=1}^n i \lfloor \frac{k}{i} \rfloor \]

考慮後面的式子:

一開始 \(l=1\)

我們設 \(\lfloor \frac{k}{l} \rfloor=t\) ,分兩種情況:

  1. \(t \not ={0},r=min(\lfloor \frac{k}{t} \rfloor,n)\);

  2. \(t= 0 ,r=n\).

然後如果沒有到邊界,直接 \(l=r+1\) ,再次計算 \(\lfloor \frac{k}{l} \rfloor\) 即可。

此時,這一段的答案就是:

\(\frac{(l+r)}{2}\)( 原式子中 \(i\) 的中間值) \(\times (r-l+1)\) (該區間長度) \(\times \lfloor \frac{k}{l} \rfloor\) (對應的值)

最後跳出迴圈,輸出即可。

程式碼:

// 「luogu P2261」[CQOI2007]餘數求和

#include<bits/stdc++.h>
using namespace std;
#define int long long 
int n,k;
int l,r,ans;
signed main(){
    cin>>n>>k;
    ans=k*n;
    for(int l=1;l<=n;l=r+1){
        int now=k/l;
        if(now!=0) r=min(k/now,n);
        else r=n;
        ans-=(r-l+1)*(l+r)/2*(k/l);
    }
    cout<<ans<<endl;
    system("pause");

    return 0;
}