分块

分块显然是众多数据结构中最通用,最容易被卡掉也是出题人最希望卡掉的作法。在一众神犇的奇思妙想下,大部分的 \(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;
}
posted @ 2024-10-09 18:29  Zzzzzzzm  阅读(10)  评论(0)    收藏  举报