分块
分块显然是众多数据结构中最通用,最容易被卡掉也是出题人最希望卡掉的作法。在一众神犇的奇思妙想下,大部分的 \(DS\) 都被各种分块卡过了,甚至一些较为复杂的树套树等等都是分块可能可以卡的作法。
分块的精髓在于块的大小,块大小为 \(\sqrt{n}\) 是最为常见的,因为它此时平衡了操作与查询之间的时间复杂度,当然在部分分块题中也会有别的长度的分块。
整除分块
思路
整除分块其实与数论更为接近,虽然但是它只是为下一步的数据结构做准备罢了,所以将其归入了数据结构。
当问题中出现了对于 \(\sum_{i=1}^n{\lfloor\frac{n}{i}\rfloor}\) 的区间操作,显然此时是能 \(O(n)\) 求解的,但是在部分题目中为了保证时间复杂度,需要 \(O(\sqrt{n})\) 求解,可以采用整数分块。
for(register int l, r; l <= n; l = r+1){
r = min(n/(n/l), n);
ans += (r-l+r)*(n/l);
}
此时 \(ans=\sum_{i=1}^n{\lfloor\frac{n}{i}\rfloor}\)。
这是十分显而易见的,此时 \(\lfloor\frac{n}{l}\rfloor=\lfloor\frac{n}{r}\rfloor\) 且 \(\lfloor\frac{n}{r}\rfloor\neq\lfloor\frac{n}{r+1}\rfloor\)。
证明是显然的,\(\lfloor\frac{n}{\lfloor\frac{n}{l}\rfloor}\rfloor)\) 显然是 \(n/i\) 中最大的那个,且不超过 \(\sqrt{n}\) 个。
例题一:余数求和
思路
整除分块裸题。
\(Code\)
#include<bits/stdc++.h>
using namespace std;
long long n, k;
template<typename T>
inline void read(T&x){
x = 0; char q; bool f = 1;
while(!isdigit(q = getchar())) if(q == '-') f = 0;
while(isdigit(q)){
x = (x<<3) + (x<<1) + (q^48);
q = getchar();
}
x=f?x:-x;
}
template<typename T>
inline void write(T x){
if(x < 0){
putchar('-');
x = -x;
}
if(x > 9) write(x/10);
putchar(x%10+'0');
}
int main(){
read(n), read(k);
long long ans = n * k;
for(long long l = 1, r; l <= n; l = r + 1){
if(k / l != 0) r = min(k / (k / l), n);
else r = n;
ans -= (k / l) * (r - l + 1) * (l + r) / 2;
}
write(ans);
return 0;
}

浙公网安备 33010602011771号