【數論】數論分塊
【數論】數論分塊
求解圖中紅點和綠點的總數之和。
求$$\sum_{i=1}^{n}k \space mod\space i$$?
如果縱軸的x一個點一個點進行移動的話,將會非常緩慢。
前置知識
\(k\%i=k-[\frac{k}i]\times i\)
於是有\(\sum_{i=1}^{n}k \space mod\space i=\sum_{i=1}^{n}k-[\frac{k}i]\times i=n\times k-\sum_{i=1}^{n}i\times[\frac{k}{i}]\)
所以我們重點是要去求取\(\sum_{i=1}^{n}i\times[\frac{k}{i}]\)
若在一段區間內\([\frac{k}{i}]\)保持不變,可以將其看作是一個常數,而i是線性變化的,於是我們就可以通過等差數列的求和公式求出這一段區間的值。
而數論的分塊(暫時的理解)是快速通過這些區間的左端點來求出區間的右端點,從而降低時間損耗。
就如圖上的紅點變成綠點,而不需要把每一個點都計算出答案。
x | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
---|---|---|---|---|---|---|---|---|---|---|---|
\([\frac{11}{x}]\) | 11 | 5 | 3 | 2 | 2 | 1 | 1 | 1 | 1 | 1 | 1 |
對於任意一個i,求一個最大的j使得\([\frac{n}{i}]=[\frac{n}{j}]\)
且此時\(j=[\frac{n}{[\frac{n}{i}]}]\)
證明
令\(k=[\frac{n}{i}]<=\frac{n}{i}\) 。(k為商,即矩形所對應的高度)
-
則有\(j=[\frac{n}{k}]>=[\frac{n}{\frac{n}{i}}]=[i]=i\),所以j>=i(證明j在這個值下大於任意一個i)
-
前提引入,\([\frac{n}{i}]=[\frac{n}{j}]\)
進而在替換後有,\([\frac{n}{j}]=k\),所以有\(k<=\frac{n}{j}<k+1\),進一步有\(\frac{n}{k+1}<j<=\frac{n}{k}\)
j為整數,進而有\([\frac{n}{k}]=j_{max}\)
複雜度證明
通過打表不難發現,\(\lfloor\frac ni\rfloor\)最多有$2\cdot\sqrt n \(種取值,所以,整除分塊的複雜度為\)O(\sqrt n)$。
證明:
若\(i\le\sqrt n\),顯然最多有\(\sqrt n\)種取值(i的取值都只有\(\sqrt n\)種)。
若\(i>\sqrt n\),則\(\lfloor\frac ni\rfloor<\sqrt n\),取值亦不超過\(\sqrt n\)種。
模板題
P2261 [CQOI2007]餘數求和
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define MAX 1000005
#define MOD 1000000007
using namespace std;
const int N = 3E5+5,M = 6E5+10;
ll n,k;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>k;
ll ans = n * k;
for(ll l=1,r;l<=n;l=r+1)
{
if(k/l!=0) r = min(k/(k/l),n);
else r = n;
ans -= (r-l+1)*(l+r)*(k/l)/2;
}
cout<<ans;
return 0;
}