天天看點

洛谷P4168 [Violet]蒲公英 題解 數列分塊

題目大意:對大小為 \(n\) 的數列進行 \(m\) ,每次求出區間最小衆數。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 40040;

int n, m, blo, id, a[maxn], bl[maxn], p[202][202];
map<int, int> mp;
int val[maxn], cnt[maxn];
vector<int> g[maxn];

void pre(int x) {
    memset(cnt, 0, sizeof(cnt));
    int res = 0, mx = 0;
    for (int i = (x-1)*blo+1; i <= n; i ++) {
        cnt[a[i]] ++;
        int t = bl[i];
        if (cnt[a[i]] > mx || cnt[a[i]] == mx && val[a[i]] < val[res]) {
            res = a[i];
            mx = cnt[a[i]];
        }
        p[x][t] = res;
    }
}

int mycount(int l, int r, int x) {
    return upper_bound(g[x].begin(), g[x].end(), r) - lower_bound(g[x].begin(), g[x].end(), l);
}

int query(int l, int r) {
    int res = p[bl[l]+1][bl[r]-1], mx = mycount(l, r, res);
    for (int i = l; i <= min(bl[l]*blo, r); i ++) {
        int t = mycount(l, r, a[i]);
        if (t > mx || t == mx && val[a[i]] < val[res]) {
            res = a[i];
            mx = t;
        }
    }
    if (bl[l] != bl[r]) {
        for (int i = (bl[r]-1)*blo+1; i <= r; i ++) {
            int t = mycount(l, r, a[i]);
            if (t > mx || t == mx && val[a[i]] < val[res]) {
                res = a[i];
                mx = t;
            }
        }
    }
    return res;
}

int main() {
    ios::sync_with_stdio(0);
    cin >> n >> m;
    blo = sqrt(n);
    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
        bl[i] = (i - 1) / blo + 1;
        if (!mp[a[i]]) {
            mp[a[i]] = ++id;
            val[id] = a[i];
        }
        a[i] = mp[a[i]];
        g[a[i]].push_back(i);
    }
    for (int i = 1; i <= bl[n]; i ++)
        pre(i);
    int l, r, x = 0;
    for (int i = 1; i <= m; i ++) {
        int l0, r0;
        cin >> l0 >> r0;
        l = (l0 + x -1) % n + 1;
        r = (r0 + x - 1) % n + 1;
        if (l > r) swap(l, r);
        x = val[query(l, r)];
        cout << x << endl;
    }
    return 0;
}