「Ynoi2019 模拟赛」Yuno loves sqrt technology III

区间众数。

发现如果可以离线莫队特别好做,那按理来说莫队能做的分块都能做,所以考虑分块。

做分块就先考虑一些经典东西:\(val_{i, j}\) 表示块 \(i\) 到块 \(j\) 的众数出现次数。

对于查询 \([l, r]\),我们已经获取了块 \([bel_l + 1, bel_r - 1]\) 的众数出现次数 \(cnt\),发掘该信息对答案有什么用,因为只剩下 \(2 \sqrt{N}\) 个数没统计,那么答案最多比 \(cnt\)\(2 \sqrt{N}\),且若比答案更大,众数一定出现在没统计到的数中,即块 \(bel_l\)\(bel_r\) 中没被统计的数,然后我们对于某个数可以 \(O(1)\) check该数在区间内出现次数与答案大小关系,用 \(O(V)\)vector存下每种值的出现位置,记 \(p_v\) 按顺序存下值 \(v\) 的所有出现位置,假设当前位置为 \(p_{v, i}\),直接看 \(p_{v, i + ans - 1}\) 是否 \(\leq r\) 即可 check。

然后因为答案 \(\in [cnt, cnt + 2 \sqrt{N}]\),所以答案最多只会被 check 成功 \(2\sqrt{N}\) 次,而两边散块每个可能的众数只会 check 失败 \(1\) 次,总计最多 \(2 \sqrt{N}\) 次,所以先将 \(ans\) 设为 \(cnt\),暴力对两边散块 check \(ans + 1\),check 成功就将 \(ans\) 加一,时间复杂度 \(O(N \sqrt{N})\),空间复杂度 \(O(N)\)

/*
address:https://www.luogu.com.cn/problem/P5048
AC 2026/9/2 21:07
*/
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
const int S = 710;
int n, q, a[N], disc[N], m;
inline void read(int& x) {
    x = 0;
    char c = getchar();
    while (c < '0' || c > '9') c = getchar();
    while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
}
vector<int>vec[N];
int rnk[N];
int lastans;
int val[S][S];
int bel[N], L[S], R[S];
int siz, blk;
int cnt[N];
bool vis[N];
int main() {
    read(n), read(q);
    for (int i = 1;i <= n;++i) read(a[i]), disc[++m] = a[i];
    sort(disc + 1, disc + m + 1);
    m = unique(disc + 1, disc + m + 1) - disc - 1;
    for (int i = 1;i <= m;++i) vec[i].push_back(0);
    for (int i = 1;i <= n;++i) vec[a[i] = lower_bound(disc + 1, disc + m + 1, a[i]) - disc].push_back(i), rnk[i] = vec[a[i]].size() - 1;
    for (int i = 1;i <= m;++i) vec[i].push_back(n + 1);
    siz = sqrt(n);
    blk = (n + siz - 1) / siz;
    for (int i = 1;i <= n;++i) bel[i] = (i + siz - 1) / siz;
    for (int i = 1;i <= blk;++i) L[i] = (i - 1) * siz + 1, R[i] = min(n, i * siz);
    for (int i = 1;i <= blk;++i) {
        for (int j = 1;j <= m;++j) cnt[j] = 0;
        for (int j = 0;j <= n;++j) vis[j] = false;
        int cur = 0;
        for (int j = i;j <= blk;++j) {
            for (int k = L[j];k <= R[j];++k) {
                vis[++cnt[a[k]]] = true;
                if (vis[cur + 1]) ++cur;
            }
            val[i][j] = cur;
        }
    }
    while (q--) {
        int l, r;read(l), read(r);
        l ^= lastans, r ^= lastans;
        int p = bel[l], q = bel[r];
        if (p + 1 >= q) {
            lastans = 0;
            for (int i = l;i <= r;++i) cnt[a[i]] = 0;
            for (int i = 0;i <= r - l + 1;++i) vis[i] = false;
            for (int i = l;i <= r;++i) {
                vis[++cnt[a[i]]] = true;
                if (vis[lastans + 1]) ++lastans;
            }
        }
        else {
            lastans = val[p + 1][q - 1];
            for (int i = l;i <= R[p];++i)
                if (rnk[i] + lastans < vec[a[i]].size())
                    while (vec[a[i]][rnk[i] + lastans] <= r) ++lastans;
            for (int i = L[q];i <= r;++i)
                if (rnk[i] - lastans >= 0)
                    while (vec[a[i]][rnk[i] - lastans] >= l) ++lastans;
        }
        printf("%d\n", lastans);
    }
    return 0;
}
posted @ 2026-09-02 22:10  keysky  阅读(5)  评论(0)    收藏  举报