题解:P13984 数列分块入门 9
为啥没有正常做法。
::::info[题意]
给出长度为
以
对于一个询问,如果左右端点同块就暴力做掉。否则可以用 basic_string 存储每种颜色出现位置,记录一下每个位置是这个颜色第几次出现,则可以知道从它开始往前、往后若干个位置在哪里。判断对应位置是否在区间内即可。每插入一个数就不断 check 当前出现次数
时间复杂度
鉴于这是模板题,所以放一个代码。建议 3s 64MB。
::::success[AC Code]
#include <bits/stdc++.h>
using namespace std; const int N = 300005, K = 705; basic_string<int> p[N];
int n, a[N], B, be[N], b[N], m, id[N], bl[K], br[K], f[K][K], c[N], g[K][K];
int Q(int l, int r) {
int u = 0, v;
if (be[l] == be[r]) {
for (int i = l; i <= r; ++i) {
++c[a[i]];
if (c[a[i]] > u || c[a[i]] == u && a[i] < v) u = c[a[i]], v = a[i];
}
for (int i = l; i <= r; ++i) c[a[i]] = 0; return v;
}
if (be[l] + 1 < be[r])
u = f[be[l] + 1][be[r] - 1], v = g[be[l] + 1][be[r] - 1];
for (int i = br[be[l]], t; i >= l; --i) {
auto chk = [&](int o) {
return id[i] + o < p[a[i]].size() && p[a[i]][id[i] + o - 1] <= r;
};
t = u; while (chk(t + 1)) ++t;
if (t > u) v = a[i];
else if (chk(u) && a[i] < v) v = a[i];
u = t;
}
for (int i = bl[be[r]], t; i <= r; ++i) {
auto chk = [&](int o) {
return id[i] + 1 >= o && p[a[i]][id[i] - o + 1] >= l;
};
t = u; while (chk(t + 1)) ++t;
if (t > u) v = a[i];
else if (chk(u) && a[i] < v) v = a[i];
u = t;
}
return v;
}
int main() {
scanf("%d", &n); B = sqrt(n);
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]), be[i] = (i - 1) / B + 1, b[i] = a[i];
stable_sort(b + 1, b + n + 1); m = unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= m; ++i) p[i] += 0;
for (int i = 1; i <= n; ++i)
a[i] = lower_bound(b + 1, b + m + 1, a[i]) - b,
id[i] = p[a[i]].size(), p[a[i]] += i;
for (int i = 1; i <= m; ++i) p[i] += n + 1;
for (int i = 1; i <= be[n]; ++i)
bl[i] = br[i - 1] + 1, br[i] = min(n, i * B);
for (int i = 1; i <= be[n]; ++i) {
for (int j = bl[i], k = i, u = 0, v; j <= n; ++j) {
++c[a[j]];
if (c[a[j]] > u || c[a[j]] == u && a[j] < v) u = c[a[j]], v = a[j];
if (j == br[k]) f[i][k] = u, g[i][k] = v, ++k;
}
memset(c, 0, sizeof c);
}
for (int i = 1, l, r; i <= n; ++i)
scanf("%d%d", &l, &r), printf("%d\n", b[Q(l, r)]);
return 0;
}
::::