思路
其实维护不同操作的分块,它的核心代码依旧是【排序 】与【l , r l, r l,r 移动 】的那几行。不同的就是 a d d ( i ) , d e l ( i ) add(i), del(i) add(i),del(i) 中的数据结构。
我们看到要求最大连续段,就应该可以想到用线段树来维护。
线段树要满足以下操作:
- 单点修改某个值 :就是用 0 / 1 0/1 0/1 表示当前区间是否存在某个数;
- 合并区间最长连续段:记录每个区间的【最长连续段 m x [ o ] mx[o] mx[o]】【最长连续前缀 p r e [ o ] pre[o] pre[o]】【最长连续后缀 s u f [ o ] suf[o] suf[o]】,这几个数组在单点修改时维护即可;
- 求值域在 [ 1 , n ] [1, n] [1,n] 内的最长连续段:返回 m x [ 1 ] mx[1] mx[1] 即可。
时间复杂度:移动一次的复杂度为 O ( l o g ( n ) ) O(log(n)) O(log(n)),总得时间复杂度就是 O ( n n × l o g ( n ) ) O(n \sqrt{n} \times log(n)) O(nn ×log(n))。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e4 + 7;
int n, m, a[maxn];
struct query {int l, r, id;} q[maxn];
#define id(x) (((x) - 1) / sz + 1)
int sz, l, r;
bool cmp(query x, query y) {
if (id(x.l) != id(y.l)) return id(x.l) < id(y.l);
if (id(x.l) & 1) return x.r < y.r;
return x.r > y.r;
}
#define ls (o << 1)
#define rs (o << 1 | 1)
#define mid ((l + r) >> 1)
struct SegmentTree {
int mx[maxn << 2], pre[maxn << 2], suf[maxn << 2];
void mdf(int o, int l, int r, int q, int v) {
if (l == r) {
mx[o] += v;
pre[o] += v;
suf[o] += v;
return ;
}
if (q <= mid) mdf(ls, l, mid, q, v);
else mdf(rs, mid + 1, r, q, v);
// 合并求最长段
pre[o] = pre[ls], suf[o] = suf[rs];
if (mx[ls] == mid - l + 1) pre[o] = mx[ls] + pre[rs];
if (mx[rs] == r - mid) suf[o] = mx[rs] + suf[ls];
mx[o] = max(suf[ls] + pre[rs], max(mx[ls], mx[rs]));
}
} sg;
inline void add(int x) {sg.mdf(1, 1, n, a[x], 1);}
inline void del(int x) {sg.mdf(1, 1, n, a[x], -1);}
int ans[maxn];
int main() {
scanf("%d%d", &n, &m), sz = sqrt(n);
for (int i = 1; i <= n; ++i) scanf("%d", a + i);
for (int i = 1; i <= m; ++i) scanf("%d%d", &q[i].l, &q[i].r), q[i].id = i;
sort(q + 1, q + m + 1, cmp);
l = q[1].l, r = q[1].r;
for (int i = l; i <= r; ++i) add(i);
ans[q[1].id] = sg.mx[1];
for (int i = 2; i <= m; ++i) {
while (l < q[i].l) del(l++);
while (l > q[i].l) add(--l);
while (r < q[i].r) add(++r);
while (r > q[i].r) del(r--);
ans[q[i].id] = sg.mx[1];
}
for (int i = 1; i <= m; ++i) printf("%d\n", ans[i]);
return 0;
}