题目描述
难度分:1370
输入n(1≤n≤2×105)、q(1≤q≤2×105)和长为n的数组a(1≤a[i]≤109)。下标从1开始。然后输入q个询问,格式如下:
1 p x
:把a[p]改成x(1≤x≤109)。2 l r
:输出a的子数组[l,r]中的严格次大值的个数。
注:严格次大值指不等于最大值的数中的最大值。
输入样例1
5 4
3 3 1 4 5
2 1 3
2 5 5
1 3 3
2 2 4
输出样例1
1
0
2
输入样例2
1 1
1000000000
2 1 1
输出样例2
0
输入样例3
8 9
2 4 4 3 9 1 1 2
1 5 4
2 7 7
2 2 6
1 4 4
2 2 5
2 2 7
1 1 1
1 8 1
2 1 8
输出样例3
0
1
0
2
4
算法
线段树
比较明显的一道数据结构题目,单点修改区间查询比较容易想到线段树。关键是每个节点要维护的信息,线段树的节点维护以下5个信息:
- 区间左端点l;
- 区间右端点r;
- 区间最大值y1;
- 区间次大值y2;
- 区间最大值和次大值的频数表counter。
只需要实现左右孩子lchild和rchild的信息合并,显然info.l=lchild.l,info.r=rchild.r。把左右孩子的counter表拿出来融合,取出最大的两个值及其频数插入到info.counter中,并更新info.y1和info.y2(由于lchild.counter和rchild.counter两个表的大小最大就是2),所以这个操作是常数级别的,只是常数比较大。如果info.counter的大小<2,说明区间[l,r]没有严格次大值,y2=0。
复杂度分析
时间复杂度
建立线段树的时间复杂度为O(nlog2n);每个询问需要对线段树进行修改或查询操作,时间复杂度为O(log2n),q个询问的时间复杂度就是O(qlog2n)。因此,整个算法的时间复杂度为O((n+q)log2n)。
空间复杂度
除了输入的数组a,空间消耗的瓶颈就在于线段树。因此,整个算法的额外空间复杂度为O(n)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
int n, q, a[N];
class SegmentTree {
public:
struct Info {
int l, r, y1, y2;
unordered_map<int, int> counter;
Info() {}
Info(int left, int right, int val): l(left), r(right) {
y1 = val;
y2 = 0;
counter[y1] = 1;
}
} seg[N<<2];
explicit SegmentTree() {}
void build(int u, int l, int r) {
if(l == r) {
seg[u] = Info(l, r, a[r]);
}else {
int mid = l + r >> 1;
build(u<<1, l, mid);
build(u<<1|1, mid + 1, r);
pushup(u);
}
}
void modify(int pos, int val) {
modify(1, pos, val);
}
Info query(int l, int r) {
if(l > r) return Info(0, 0, 0);
return query(1, l, r);
}
private:
void modify(int u, int pos, int val) {
if(seg[u].l == pos && seg[u].r == pos) {
seg[u] = Info(pos, pos, val);
}else {
int mid = seg[u].l + seg[u].r >> 1;
if(pos <= mid) {
modify(u<<1, pos, val);
}else {
modify(u<<1|1, pos, val);
}
pushup(u);
}
}
Info query(int u, int l, int r) {
if(l <= seg[u].l && seg[u].r <= r) return seg[u];
int mid = seg[u].l + seg[u].r >> 1;
if(r <= mid) {
return query(u<<1, l, r);
}else if(mid < l) {
return query(u<<1|1, l, r);
}else {
return merge(query(u<<1, l, r), query(u<<1|1, l, r));
}
}
void pushup(int u) {
seg[u] = merge(seg[u<<1], seg[u<<1|1]);
}
Info merge(const Info& lchild, const Info& rchild) {
Info info;
info.l = lchild.l;
info.r = rchild.r;
auto& lcnt = lchild.counter;
auto& rcnt = rchild.counter;
unordered_map<int, int> counter;
for(auto&[num, freq]: lcnt) {
counter[num] += freq;
}
for(auto&[num, freq]: rcnt) {
counter[num] += freq;
}
vector<array<int, 2>> tup;
for(auto&[num, freq]: counter) {
tup.push_back({num, freq});
}
sort(tup.begin(), tup.end(), [&](auto&x, auto&y) {
return x[0] > y[0];
});
int sz = tup.size();
if(sz >= 2) {
info.y1 = tup[0][0];
info.y2 = tup[1][0];
info.counter[info.y1] = tup[0][1];
info.counter[info.y2] = tup[1][1];
}else {
info.y1 = tup[0][0];
info.y2 = 0;
info.counter[info.y1] = tup[0][1];
}
return info;
}
};
int main() {
scanf("%d%d", &n, &q);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
SegmentTree tr;
tr.build(1, 1, n);
while(q--) {
int type;
scanf("%d", &type);
if(type == 1) {
int p, x;
scanf("%d%d", &p, &x);
tr.modify(p, x);
}else {
int l, r;
scanf("%d%d", &l, &r);
auto info = tr.query(l, r);
int y2 = info.y2;
printf("%d\n", y2 == 0? y2: info.counter[y2]);
}
}
return 0;
}