Luogu P3793 由乃救爷爷
分块乱搞,设块大小为 。
我们暴力预处理每个块前缀、后缀的最大值,用 ST表 求出任意连续块之间的最大值。
预处理复杂度为 。
查询的时候找中间夹着的块的最大值、左侧后缀和右侧前缀。
查询显然是 的。
但是这个做法有问题。当左右端点在同一块内时就假了。
这一题数据随机,所以我们可以暴力,并且复杂度是对的。
两个端点在同一块内的概率是 ,每次查询的代价是 ,所以期望为 。
令 每次暴力查询期望复杂度为 。
这样我们就得到了一个 预处理, 查询的做法。
#include <algorithm>
#include <cmath>
#include <cstdio>
using namespace std;
const int N = 2e7 + 5;
int n, m, s, block;
int st[N/100][25], a[N];
int suf[N], pre[N];
unsigned long long ans;
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);
}
} // namespace GenHelper
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;
}
int getpos(int x) { return (x + block - 1) / block; }
int bf(int l, int r) {
int res = a[l];
for (int i = l; i <= r; i++)
res = max(res, a[i]);
return res;
}
void init() {
int p = getpos(n);
for (int len = 1; len <= 20; len++)
for (int i = 1; i <= p - (1 << len) + 1; i++)
st[i][len] = max(st[i][len - 1], st[i + (1 << (len - 1))][len - 1]);
for (int i = 1; i <= n; i++)
if (i % block != 1)
pre[i] = max(pre[i - 1], a[i]);
else
pre[i] = a[i];
for (int i = n; i >= 1; i--)
if (i % block)
suf[i] = max(suf[i + 1], a[i]);
else
suf[i] = a[i];
}
int query(int l, int r) {
int e = log2(r - l - 1);
int res = max(st[l + 1][e], st[r - (1 << e)][e]);
return res;
}
int main() {
scanf("%d%d%d", &n, &m, &s);
srand(s);
block = sqrt(n);
for (int i = 1; i <= n; i++) {
int j = getpos(i);
a[i] = read();
st[j][0] = max(a[i], st[j][0]);
}
init();
for (int x, y; m; m--) {
x = read() % n + 1, y = read() % n + 1;
if (x > y)
swap(x, y);
int l = getpos(x), r = getpos(y);
int mx = max(suf[x], pre[y]);
if (l == r)
ans += bf(x, y);
else if (r - l == 1)
ans += mx;
else
ans += max(mx, query(l, r));
}
printf("%llu\n", ans);
return 0;
}
拓展:
刚才说这个算法在数据随机的情况下是对的。
但实际上数据不随机也不太可能被卡。
要卡掉这个算法,就肯定要让区间尽可能在块内,让复杂度为 。
咕咕咕
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现