推一下自己的莫比烏斯反演:Link
數論分塊
對於一類含有\(\left\lfloor\frac{n}{i}\right\rfloor\)的求和式 (\(n\) 為常數),由於\(\left\lfloor\frac{n}{i}\right\rfloor\)單調不增,故存在多個區間\([l,r]\), 使得\(\left\lfloor\frac{n}{i}\right\rfloor = \left\lfloor\frac{n}{j}\right\rfloor(i,j\in [l,r])\) 成立。
對於任意一個\(i\),最大的滿足上式的 \(j=\left\lfloor{\dfrac{n}{\left\lfloor{\dfrac{n}{i}}\right\rfloor}}\right\rfloor\)
證明法1
對於滿足\(\left\lfloor\frac{n}{i}\right\rfloor = \left\lfloor\frac{n}{j}\right\rfloor\)的 \(j(i<j)\),有:
則有:
即有:
又 \(j \in \mathbb{N}\),則 \(j\)最大值為\(\left\lfloor{\dfrac{n}{\left\lfloor\frac{n}{i}\right\rfloor}}\right\rfloor\)
證明法2
即 \(j = \left\lfloor\dfrac{n}{\left\lfloor\dfrac{n}{i}\right\rfloor}\right\rfloor\)
復雜度分析
引理
\(|V|\)表示集合\(V\)的元素個數
證明
當\(d\le \left\lfloor\sqrt{n}\right\rfloor\),\(\left\lfloor\dfrac{n}{d}\right\rfloor\)最多有\(\left\lfloor\sqrt{n}\right\rfloor\)種取值。
當\(d\ge \left\lfloor\sqrt{n}\right\rfloor\),有\(\left\lfloor\dfrac{n}{d}\right\rfloor \le \left\lfloor\sqrt{n}\right\rfloor\),\(\left\lfloor\dfrac{n}{d}\right\rfloor\)最多有\(\left\lfloor\sqrt{n}\right\rfloor\)種取值。
\(\left\lfloor\dfrac{n}{d}\right\rfloor\)兩兩不同時集合大小取最大值。
復雜度 \(O(\sqrt n)\)。
例1 [AHOI2005]約數研究
令 \(f(i)\) 為 \(i\) 的約數個數,求
\[\sum\limits_{i=1}^{n} f(i) \]\(n \le 10^6\)
對於 \(i\), 在\(1\sim n\) 中其倍數個數為\(\left\lfloor\dfrac{n}{i}\right\rfloor\),
則\(1\sim n\)中共有\(\left\lfloor\dfrac{n}{i}\right\rfloor\) 個以其為約數的數。
則\(\sum\limits_{i=1}^{n} f(i) = \sum\limits_{i=1}^{n} \left\lfloor\dfrac{n}{i}\right\rfloor\)
此時直接\(O(n)\)暴力即可通過本題。
若 \(n\le 10^{14}\),考慮數論分塊:
由上可知,對於每一個\(l\in [1,n]\), 存在區間\([l, r], r = \left\lfloor\dfrac{n}{\left\lfloor\dfrac{n}{l}\right\rfloor}\right\rfloor\),
使得\(\left\lfloor\dfrac{n}{i}\right\rfloor = \left\lfloor\dfrac{n}{j}\right\rfloor(i,j\in [l,r])\),
區間\([l,r]\) 貢獻即為\((r-l+1)\times \left\lfloor\dfrac{n}{l}\right\rfloor\)。
枚舉這樣的\(j\)計算貢獻即可,復雜度\(O(2\sqrt{n})\)
//By:Luckyblock
#include <cstdio>
#include <cctype>
#define ll long long
//=============================================================
int N, ans;
//=============================================================
inline int read()
{
int f = 1, w = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
//=============================================================
int main()
{
N = read();
for(int i = 1, j; i <= N; i = j + 1)
{
j = N / (N / i);
ans += (j - i + 1) * (N / i);
}
printf("%d\n", ans);
return 0;
}
例二 [CQOI2007]余數求和
給定\(n, k\),求
\[\sum\limits_{i=1}^{n} {k\!\!\!\mod\! i} \]\(n,k \le 10^9\)
進行簡單的轉化:
同例一,存在多段\(\left\lfloor\dfrac{k}{i}\right\rfloor\)相等的區間\([l,r]\),其貢獻為 \(-\left\lfloor\dfrac{k}{i}\right\rfloor \times \sum\limits_{i=l}^{r} i\)。
通過數論分塊 \(+\) 等差數列求和 得到答案,復雜度\(O(2\sqrt{n})\)。
//By:Luckyblock
#include <cstdio>
#include <cctype>
#define ll long long
//=============================================================
ll N, K, Ans;
//=============================================================
inline ll read()
{
ll f = 1, w = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
ll GetSum(ll L, ll R) {return (R - L + 1ll) * (L + R) / 2ll;} //等差數列求和
//=============================================================
int main()
{
N = read(), K = read(), Ans = N * K;
for(ll i = 1, j; i <= N; i = j + 1ll)
{
if(K / i == 0) break; //當k/i = 0時,對答案無貢獻
j = K / (K / i); j = j > N ? N : j;
Ans -= (K / i) * GetSum(i, j);
}
printf("%lld", Ans);
return 0;
}
例三 約數和
定義\(f(n) = \sum\limits_{i\mid n} i\),給定\(x, y\),求
\[\sum\limits_{i=x}^{y}f(i) \]\(x,y \le 2\times 10^9\)
將答案變為前綴和形式:
\(\sum\limits_{i=x}^{y}f(i) = \sum\limits_{i=1}^{y}f(i) - \sum\limits_{i=1}^{x - 1}f(i)\)
簡單轉化 \(\sum\limits_{i=1}^{n} f(i) = \sum\limits_{i=1}^{n} \sum\limits_{k\mid i} k\)
變換求和順序,上式即為 \(\sum\limits_{k=1}^{n} \sum\limits_{k\mid i} k (i\le n)\)
\(\sum\limits_{k\mid i}k\) 等價於枚舉 \(1\sim n\) 中約數為 \(k\) 的數,
由例一,上式即為 \(\sum\limits_{k=1}^{n} k\times \left\lfloor\dfrac{n}{k}\right\rfloor\)
同例二,通過數論分塊 \(+\) 等差數列求和 得到答案,復雜度\(O(2\sqrt{n})\)。
//By:Luckyblock
#include <cstdio>
#include <cctype>
#include <algorithm>
#define ll long long
//=============================================================
ll x, y;
//=============================================================
inline ll read()
{
ll f = 1, w = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
ll GetSum(ll L, ll R) {return (L + R) * (R - L + 1) / 2;}
ll GetAns(ll N)
{
ll ret = 0;
for(ll l = 1, r; l <= N; l = r + 1)
{
r = N / (N / l);
ret += GetSum(l, r) * (N / l);
}
return ret;
}
//=============================================================
int main()
{
x = read(), y = read();
printf("%lld", GetAns(y) - GetAns(x - 1));
return 0;
}
寫在最后
數論分塊屬於小工具一類的知識點。
推式子時要盡量將式子轉化為類似形式上。
證明法1是自己yy的,有不當之處請不吝賜教。
參考資料:
初涉數論分塊 - AntiQuality - 博客園
莫比烏斯反演 - OI Wiki
無主之地3真好玩,快去買。
