题意
传送门 P3709 大爷的字符串题
题解
求区间内元素最少能组成多少个严格上升子序列,其个数只与相同值的数字有关;即求解区间众数。
使用莫队算法求解。将 a i a_i ai 离散化,维护区间内数字 x x x 的个数 c n t x [ x ] cnt_x[x] cntx[x],以及个数为 k k k 的数字数量 c n t k [ k ] cnt_k[k] cntk[k],即可 O ( 1 ) O(1) O(1) 更新区间出现次数最多的数字的数量。总时间复杂度 O ( N M ) O(N\sqrt M) O(NM)。
需要注意,莫队修改左右界时需要保证合理性,故应保证区间修改顺序的合理性,即先拓展区间,再缩减区间。奇偶性排序优化需对应分块的最后一维编号,以保证块的相邻。
实现上,需要注意逻辑运算符的优化,在表达式成立时会导致后几项运算直接被跳过;若将需要执行的自增、自减等运算写入逻辑判断语句,需要注意放在逻辑表达式的第一项。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 200005;
int N, M, X[maxn], xn, xs[maxn];
int res, id[maxn], cnt_x[maxn], cnt_k[maxn], rec[maxn];
struct node
{
int l, r, k;
bool operator<(const node &b) const
{
if (id[l] != id[b.l])
return l < b.l;
return (id[l] & 1) ? r < b.r : r > b.r;
}
} Q[maxn];
inline void add(int i)
{
int &k = cnt_x[X[i]];
if (res == k)
++res;
--cnt_k[k], ++cnt_k[++k];
}
inline void del(int i)
{
int &k = cnt_x[X[i]];
--cnt_k[k];
if (!cnt_k[k] && res == k)
--res;
++cnt_k[--k];
}
int main()
{
scanf("%d%d", &N, &M);
for (int i = 1; i <= N; ++i)
scanf("%d", X + i), xs[i] = X[i];
sort(xs + 1, xs + N + 1);
xn = unique(xs + 1, xs + N + 1) - (xs + 1);
for (int i = 1; i <= N; ++i)
X[i] = lower_bound(xs + 1, xs + xn + 1, X[i]) - xs;
int w = sqrt(M), t = ceil((double)N / w);
for (int i = 1; i <= t; ++i)
for (int l = (i - 1) * w + 1, r = min(i * w, N), j = l; j <= r; ++j)
id[j] = i;
for (int i = 1, l, r; i <= M; ++i)
scanf("%d%d", &l, &r), Q[i] = node{
l, r, i};
sort(Q + 1, Q + M + 1);
for (int i = 1, l = Q[1].l, r = l - 1, ql, qr; i <= M; ++i)
{
ql = Q[i].l, qr = Q[i].r;
while (l > ql)
add(--l);
while (r < qr)
add(++r);
while (l < ql)
del(l++);
while (r > qr)
del(r--);
rec[Q[i].k] = -res;
}
for (int i = 1; i <= M; ++i)
printf("%d\n", rec[i]);
return 0;
}