P4168 [Violet]蒲公英 分块
P4168 [Violet]蒲公英
题目链接
分块。
一看到这数据范围肯定要离散化。而且强制在线,不可用莫队做。
\(p[i][j]\)表示第\(i\)个块到第\(j\)个块内的众数,预处理出来就好了,枚举\(i\)和\(j\)是\(O(\sqrt n)\)的,枚举\(j\)块内的数也是\(O(\sqrt n)\)的,总复杂度\(O(n\sqrt n)\)。
\(sum[i][h]\)表示前\(i\)个块数字\(h\)出现的个数,预处理是\(O(n)\)的。
对于每一个查询,假设左端点\(l\)所在的块是\(x\),右端点\(r\)所在的块是\(y\),然后分情况:
如果\(y - x <= 2\),直接暴力,最多扫\(3 *\sqrt n\)个数,复杂度\(O( \sqrt n)\)。
如果\(y - x > 2\),我们可以确定这个众数一定出现在两边的残块中或中间的整块中,我们只需枚举残块中的每个数,用预处理出来的\(sum\)数组求出整个区间某个数的个数,最后在与\(p[x + 1][y - 1]\),比较一下就好了,最多扫\(2 *\sqrt n\)个数,复杂度\(O( \sqrt n)\)。
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 4e4 + 5;
int n, m, t, len, last;
int a[N], c[N], vis[N], pos[N], tong[N], pre[N], sum[205][N];
struct block { int l, r; } b[N];
struct number { int num, x; } p[205][205];
void make_pre() {
for(int i = 1;i <= t; i++) {
number tmp; tmp.num = tmp.x = 0;
for(int j = i;j <= t; j++) {
for(int k = b[j].l;k <= b[j].r; k++) {
tong[a[k]]++;
if(tong[a[k]] > tmp.num) {
tmp.num = tong[a[k]]; tmp.x = a[k];
}
else if(tong[a[k]] == tmp.num) {
tmp.x = min(tmp.x, a[k]);
}
}
p[i][j] = tmp;
}
for(int j = i;j <= t; j++)
for(int k = b[j].l;k <= b[j].r; k++) tong[a[k]] = 0;
}
for(int i = 1;i <= t; i++) {
for(int j = 1;j <= n; j++) sum[i][a[j]] = sum[i - 1][a[j]];
for(int j = b[i].l;j <= b[i].r; j++) sum[i][a[j]] ++;
}
}
int query(int l, int r) {
int x = pos[l], y = pos[r];
if(y - x <= 2) {
int tmp = 0;
for(int i = l;i <= r; i++) tong[a[i]] = 0;
for(int i = l;i <= r; i++) {
tong[a[i]] ++;
if(tong[a[i]] > tong[tmp]) tmp = a[i];
else if(tong[a[i]] == tong[tmp]) tmp = min(tmp, a[i]);
}
return pre[tmp];
}
else {
int ans = p[x + 1][y - 1].x, res_num = 0, res_x = 0;
tong[ans] = vis[ans] = 0;
for(int i = l;i <= b[x].r; i++) tong[a[i]] = 0, vis[a[i]] = 0;
for(int i = b[y].l;i <= r; i++) tong[a[i]] = 0, vis[a[i]] = 0;
for(int i = l;i <= b[x].r; i++) tong[a[i]] ++;
for(int i = b[y].l;i <= r; i++) tong[a[i]] ++;
for(int i = l;i <= b[x].r; i++) {
if(!vis[a[i]]) {
vis[a[i]] = 1;
int tmp = tong[a[i]] + sum[y - 1][a[i]] - sum[x][a[i]];
if(res_num < tmp) res_num = tmp, res_x = a[i];
else if(res_num == tmp) res_x = min(res_x, a[i]);
}
}
for(int i = b[y].l;i <= r; i++) {
if(!vis[a[i]]) {
vis[a[i]] = 1;
int tmp = tong[a[i]] + sum[y - 1][a[i]] - sum[x][a[i]];
if(res_num < tmp) res_num = tmp, res_x = a[i];
else if(res_num == tmp) res_x = min(res_x, a[i]);
}
}
int tmp = tong[ans] + p[x + 1][y - 1].num;
if(res_num > tmp) ans = res_x;
else if(res_num == tmp) ans = min(ans, res_x);
return pre[ans];
}
}
int main() {
n = read(); m = read(); len = sqrt(n); t = (n - 1) / len + 1;
for(int i = 1;i <= n; i++) a[i] = c[i] = read(), pos[i] = (i - 1) / len + 1, b[pos[i]].l = 2333333;
for(int i = 1;i <= n; i++) b[pos[i]].l = min(b[pos[i]].l, i), b[pos[i]].r = max(b[pos[i]].r, i);
sort(c + 1, c + n + 1);
int cnt = unique(c + 1, c + n + 1) - c - 1;
for(int i = 1;i <= n; i++) {
int tmp = a[i];
a[i] = lower_bound(c + 1, c + cnt + 1, a[i]) - c;
pre[a[i]] = tmp;
}
make_pre();
for(int i = 1, l, r;i <= m; i++) {
l = read(); r = read();
l = (last + l - 1) % n + 1; r = (last + r - 1) % n + 1;
if(l > r) swap(l, r);
printf("%d\n", last = query(l, r));
}
return 0;
}