P3793 由乃救爷爷 题解
P3793 由乃救爷爷 题解
首先分块,对于每一个块维护一个最小值,这样是 \(m\sqrt n\) 的,无法通过此题。
考虑优化分块,注意到数据是随机的所以如果 \(l, r\) 在同一个块里面,可以直接暴力,均摊 \(O(1)\)。
证明:
\(l, r\) 在同一个块内的概率是 \(\frac{1}{\sqrt n}\),单次计算时间复杂度为 \(O(\sqrt n)\) 的,总共有 \(m\) 次询问,所以均摊总时间复杂度是 \(O(m)\)。
所以问题就变成了如何快速处理块间的最大值。
块间分为整块和散块,对于散块可以预处理出每一个块的前后缀最大值,这样预处理是 \(O(n)\) 的,查询降为 \(O(1)\),对于整块可以整一个 Sparse Table,把每一个块的最大值看成一个元素,维护块的最大值的最大值,这样可以做到预处理 \(O(\sqrt n\log(\sqrt n))\),询问 \(O(1)\)。
所以总时间复杂度为:\(O(n + m + \sqrt n\log(\sqrt n))\)
// Problem: P3793 由乃救爷爷
// Author: Moyou
// Copyright (c) 2023 Moyou All rights reserved.
// Date: 2023-08-01 08:42:51
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#define id(x) ((x - 1) / len + 1)
using namespace std;
const int N = 2.1e7 + 10, M = 5000;
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;
}
int a[N], len, tot;
int pre[M][M], suf[M][M];
int dat[M];
int st[30][M], lg[M];
void ST()
{
lg[1] = 0;
for (int i = 2; i <= tot; i++)
lg[i] = lg[i >> 1] + 1;
for (int i = 1; i <= tot; i++)
st[0][i] = dat[i];
for (int i = 1; i <= lg[tot]; i++)
for (int j = 1; j + (1 << i) - 1 <= tot; j++)
st[i][j] = max(st[i - 1][j], st[i - 1][j + (1 << i - 1)]);
}
int RMQ(int l, int r)
{
if(l > r) return 0;
int k = lg[r - l + 1];
return max(st[k][l], st[k][r - (1 << k) + 1]);
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, m, s;
cin >> n >> m >> s;
srand(s);
len = sqrt(n), tot = (n + len - 1) / len;
for(int i = 1; i <= n; i ++)
a[i] = read();
for(int i = 1; i <= tot; i ++) {
pre[i][0] = -1;
for(int j = 1; j <= len; j ++)
pre[i][j] = max(pre[i][j - 1], a[(i - 1) * len + j]);
}
for(int i = 1; i <= tot; i ++) {
dat[i] = -1;
suf[i][len + 1] = -1;
for(int j = len; j; j --)
suf[i][j] = max(suf[i][j + 1], a[(i - 1) * len + j]), dat[i] = max(dat[i], a[(i - 1) * len + j]);
}
ST();
unsigned long long ans = 0;
for(int i = 1, l, r; i <= m; i ++) {
l = read() % n + 1, r = read() % n + 1;
if(l > r) swap(l, r);
// cout << l << ' ' << r << '\n';
int res = 0;
if(id(l) == id(r)) {
while(l <= r)
res = max(res, a[l]), l ++;
}
else
res = max({suf[id(l)][l - (id(l) - 1) * len], pre[id(r)][r - (id(r) - 1) * len], RMQ(id(l) + 1, id(r) - 1)});
ans += res;
}
cout << ans << '\n';
return 0;
}