算法学习笔记(2):分块
分块
理解
优雅暴力, 对序列进行划分, 进行一些预处理, 总而减少时间复杂度, 有技术含量的是通过列式子, 再通过数学方法或者其他奇奇怪怪的方法, 求出合适的块长。
直接上题目
题解
块长定为
时间复杂度
代码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e6 + 10;
int n, q, raw[N], tmp[N];
namespace Block{
int block, L[N], R[N], tot, add[N], belong[N];
void build() {
memcpy(tmp, raw, sizeof(raw));
tot = block = sqrt(n);
for (int i = 1; i <= tot; i++) {
L[i] = (i - 1) * block + 1;
R[i] = i * block;
}
if (R[tot] < n) tot++, L[tot] = R[tot - 1] + 1, R[tot] = n;
for (int i = 1; i <= tot; i++)
sort(tmp + L[i], tmp + R[i] + 1);
for (int i = 1; i <= tot; i++)
for (int j = L[i]; j <= R[i]; j++) belong[j] = i;
}
inline void update(int l, int r, int x) {
for (int i = l; i <= r; i++) raw[i] += x;
int pos = belong[l];
for (int i = L[pos]; i <= R[pos]; i++) tmp[i] = raw[i];
sort(tmp + L[pos], tmp + R[pos] + 1);
}
void modify(int l, int r, int x) {
int p = belong[l], q = belong[r];
if (p == q) update(l, r, x);
else {
for (int i = p + 1; i <= q - 1; i++) add[i] += x;
update(l, R[p], x); update(L[q], r, x);
}
}
int query(int l, int r, int x) {
int p = belong[l], q = belong[r], res = 0;
if (p == q)
for (int i = l; i <= r; i++) raw[i] + add[p] >= x ? res++ : res = res;
else {
for (int i = p + 1; i <= q - 1; i++) {
int pos = lower_bound(tmp + L[i], tmp + R[i] + 1, x - add[i]) - tmp - 1;
res += R[i] - pos;
}
for (int i = l; i <= R[p]; i++)
if (raw[i] + add[p] >= x) res++;
for (int i = L[q]; i <= r; i++)
if (raw[i] + add[q] >= x) res++;
}
return res;
}
}
using namespace Block;
int main() {
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) scanf("%d", &raw[i]);
build();
char st[5];
for (int i = 1; i <= q; i++) {
int l, r, x;
scanf("%s%d%d%d", st, &l, &r, &x);
switch(*st) {
case 'M': modify(l, r, x); break;
case 'A': printf("%d\n", query(l, r, x)); break;
}
}
return 0;
}
题解
考虑只有查询操作, 并且只需要查询
表示第 块到第 块之间的答案。 表示前 块中编号为 的数的数量。 表示第 块到第 块之间的答案的编号。 (用来解决出现次数一样取编号较小的数)
这三个数组都提前预处理, 预处理没什么难度, 朴素的
那么对于每一次查询
时间复杂度
代码 (附debug)
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
const int N = 4e4 + 10;
const int NN = 2e2 + 10;
int n, m, raw[N], val[N], cnt;
void disc() {
memcpy(val, raw, sizeof(raw));
sort(raw + 1, raw + 1 + n);
cnt = unique(raw + 1, raw + 1 + n) - raw - 1;
for (int i = 1; i <= n; i++) {
int pos = lower_bound(raw + 1, raw + 1 + cnt, val[i]) - raw;
val[i] = pos;
}
// for (int i = 1; i <= n; i++) cout << val[i] << " ";
}
namespace Block{
int tot, block, pre[NN][N], pp[NN][NN], L[NN], R[NN], mcnt[NN][NN], belong[N];
void build() {
block = tot = sqrt(n);
for (int i = 1; i <= tot; i++) {
L[i] = (i - 1) * block + 1;
R[i] = i * block;
}
if (R[tot] < n) tot++, L[tot] = R[tot - 1] + 1, R[tot] = n;
for (int i = 1; i <= tot; i++)
for (int j = L[i]; j <= R[i]; j++)
belong[j] = i;
for (int i = 1; i <= tot; i++) {
int tmp[N] = {0};
for (int j = i; j <= tot; j++) {
pp[i][j] = pp[i][j - 1],
mcnt[i][j] = mcnt[i][j - 1];
for (int k = L[j]; k <= R[j]; k++) {
tmp[val[k]]++;
if (mcnt[i][j] < tmp[val[k]]) pp[i][j] = val[k], mcnt[i][j] = tmp[val[k]];
else if (mcnt[i][j] == tmp[val[k]] && pp[i][j] > val[k]) pp[i][j] = val[k];
}
}
}
for (int i = 1; i <= tot; i++) {
for (int j = 1; j <= cnt; j++)
pre[i][j] = pre[i - 1][j];
for (int j = L[i]; j <= R[i]; j++)
pre[i][val[j]]++;
}
}
int tmp[N], _maxs, _mcnt;
void update(int l, int r) {
for (int i = l; i <= r; i++) {
tmp[val[i]]++;
if (_mcnt < tmp[val[i]]) _maxs = val[i], _mcnt = tmp[val[i]];
else if (_mcnt == tmp[val[i]] && _maxs > val[i]) _maxs = val[i];
}
}
int query(int l, int r) {
int p = belong[l], q = belong[r];
_maxs = _mcnt = 0;
// for (int i = 1; i <= cnt; i++) tmp[i] = pre[q - 1][i] - pre[p][i];
if (p == q) {
for (int i = l; i <= r; i++) tmp[val[i]] = 0;
update(l, r);
}
else {
// for (int i = p + 1; i <= q - 1; i++)
// if (_mcnt < mcnt[i]) _maxs = maxs[i], _mcnt = mcnt[i];
// else if (_mcnt == mcnt[i] && _maxs > maxs[i]) _maxs = maxs[i];
_maxs = pp[p + 1][q - 1], _mcnt = mcnt[p + 1][q - 1];
for (int i = l; i <= R[p]; i++) tmp[val[i]] = pre[q - 1][val[i]] - pre[p][val[i]];
for (int i = L[q]; i <= r; i++) tmp[val[i]] = pre[q - 1][val[i]] - pre[p][val[i]];
update(l, R[p]); update(L[q], r);
}
return _maxs;
}
}
using namespace Block;
int main() {
// freopen("P4168_1.in", "r", stdin);
// freopen("1.out", "w", stdout);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &raw[i]);
disc(); build();
int pre = 0;
for (int i = 1; i <= m; i++) {
int l, r; scanf("%d%d", &l, &r);
l = (l + pre - 1) % n + 1, r = (r + pre - 1) % n + 1;
if (l > r) swap(l, r);
pre = raw[query(l, r)];
printf("%d\n", pre);
}
return 0;
}
/*
8 3
1 7 10 46 6 1 7 1
1 5
2 6
5 7
*/
题解
纯纯板子题, 但是数据范围
对于每一个查询
时间复杂度
代码
警钟长鸣:
st表的
就是这里if (k != -1) res = max(f[k][p + 1], f[k][q - (1 << k)]);
你若判 k != 0
就会出锅, 因为
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int n, m, s;
namespace GenHelper
{
unsigned z1,z2,z3,z4,b;
unsigned rand_()
{
b=((z1<<6)^z1)>>13;
z1=((z1&4294967294U)<<18)^b;
b=((z2<<2)^z2)>>27;
z2=((z2&4294967288U)<<2)^b;
b=((z3<<13)^z3)>>21;
z3=((z3&4294967280U)<<7)^b;
b=((z4<<3)^z4)>>12;
z4=((z4&4294967168U)<<13)^b;
return (z1^z2^z3^z4);
}
}
void srand(unsigned x)
{using namespace GenHelper;
z1=x; z2=(~x)^0x233333333U; z3=x^0x1234598766U; z4=(~x)+51;}
int read()
{
using namespace GenHelper;
int a=rand_()&32767;
int b=rand_()&32767;
return a*32768+b;
}
const int N = 2e7 + 10;
const int NN = 5e3;
const int MB = 15;
namespace Block{
int raw[N], L[NN], R[NN], block, tot, f[MB][NN], pre[NN][NN], suf[NN][NN], belong[N], len[NN] = {-1};
void build() {
tot = block = sqrt(n);
for (int i = 1; i <= tot; i++) {
L[i] = (i - 1) * block + 1;
R[i] = i * block;
}
if (R[tot] < n) tot++, L[tot] = R[tot - 1] + 1, R[tot] = n;
for (int i = 1; i <= tot; i++) len[i] = len[i / 2] + 1;
for (int i = 1; i <= tot; i++)
for (int j = L[i]; j <= R[i]; j++) belong[j] = i;
for (int i = 1; i <= tot; i++) {
for (int j = L[i]; j <= R[i]; j++) pre[i][j - L[i] + 1] = max(pre[i][j - L[i]], raw[j]);
for (int j = R[i]; j >= L[i]; j--) suf[i][R[i] - j + 1] = max(suf[i][R[i] - j], raw[j]);
f[0][i] = suf[i][R[i] - L[i] + 1];
}
for (int j = 1; j <= 13; j++)
for (int i = 1; i + (1 << (j - 1)) - 1 <= tot; i++)
f[j][i] = max(f[j - 1][i], f[j - 1][i + (1 << (j - 1))]);
}
int query(int l, int r) {
int p = belong[l], q = belong[r], res = 0;
if (p == q)
for (int i = l; i <= r; i++) res = max(res, raw[i]);
else {
int k = len[q - 1 - p];
if (k != -1) res = max(f[k][p + 1], f[k][q - (1 << k)]);
res = max(res, suf[p][R[p] - l + 1]);
res = max(res, pre[q][r - L[q] + 1]);
}
return res;
}
}
using namespace Block;
unsigned long long ans;
int main() {
scanf("%d%d%d", &n, &m, &s); srand(s);
for (int i = 1; i <= n; i++) raw[i] = read();
build();
for (int i = 1; i <= m; i++) {
int l = read() % n + 1, r = read() % n + 1;
if (l > r) swap(l, r);
ans += query(l, r);
}
printf("%llu", ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App