P3793 由乃救爷爷 题解

发布时间 2023-08-01 10:23:33作者: MoyouSayuki

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;
}