分块乱搞,设块大小为 \(len\)。
我们暴力预处理每个块前缀、后缀的最大值,用 ST表 求出任意连续块之间的最大值。
预处理复杂度为 \(O(\dfrac{n}{len}\log\dfrac{n}{len}+n+n) = O(n)\)。
查询的时候找中间夹着的块的最大值、左侧后缀和右侧前缀。
查询显然是 \(O(1)\) 的。
但是这个做法有问题。当左右端点在同一块内时就假了。
这一题数据随机,所以我们可以暴力,并且复杂度是对的。
两个端点在同一块内的概率是 \(\dfrac{len}{n}\),每次查询的代价是 \(len\),所以期望为 \(O(\dfrac{len^2}{n})\)。
令 \(len = \sqrt n\) 每次暴力查询期望复杂度为 \(O(1)\)。
这样我们就得到了一个 \(O(n)\) 预处理, \(O(1)\) 查询的做法。
#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;
}
拓展:
刚才说这个算法在数据随机的情况下是对的。
但实际上数据不随机也不太可能被卡。
要卡掉这个算法,就肯定要让区间尽可能在块内,让复杂度为 \(Olen)\)。
咕咕咕