[ABC343F] Second Largest Query
样例都过了,但是有部分点 WA 了,代码写得有点复杂了,谢谢大家帮忙。
#include <iostream>
using namespace std;
typedef pair<pair<int, int>, pair<int, int>> GRP;
const int N = 2e5 + 10;
int n, Q;
int a[N];
int max1[4*N], max2[4*N];
int cnt1[4*N], cnt2[4*N];
int compare(int lvalue, int lcnt, int rvalue, int rcnt, int &value, int &cnt){
if (lvalue > rvalue){
value = lvalue;
cnt = lcnt;
return 1;
} else if (lvalue < rvalue){
value = rvalue;
cnt = rcnt;
return 2;
} else {
value = lvalue;
cnt = lcnt + rcnt;
return 0;
}
}
void build(int l, int r, int node){ //建立记录着 l 到 r 线段上的答案的子树
if (l == r){ //最小情况:线段 l 到 r 就是一个元素,那么我们直接设定好答案
max1[node] = a[l], max2[node] = 0;
cnt1[node] = 1, cnt2[node] = 0;
return;
}
//如果 l 到 r 确实是一条线段
int mid = (l + r) >> 1; //找到线段中点
//超级操作:直接调用递归将左右子线段上的答案都求出来记录到数据结构中
build(l, mid, node << 1), build(mid + 1, r, (node << 1) + 1);
//由于左右子树都已经建好,现在可以直接拿到子问题的答案来整合出当前问题的答案
int lmax1 = max1[node << 1], rmax1 = max1[(node << 1) + 1];
int lmax2 = max2[node << 1], rmax2 = max2[(node << 1) + 1];
int con = compare(lmax1, cnt1[node << 1], rmax1, cnt1[(node << 1) + 1], max1[node], cnt1[node]);
if (con == 1) compare(lmax2, cnt2[node << 1], rmax1, cnt1[(node << 1) + 1], max2[node], cnt2[node]);
if (con == 2) compare(lmax1, cnt1[node << 1], rmax2, cnt2[(node << 1) + 1], max2[node], cnt2[node]);
if (con == 0)compare(lmax2, cnt2[node << 1], rmax2, cnt2[(node << 1) + 1], max2[node], cnt2[node]);
}
//查询在标准线段 std_l 到 std_r 中(线段结点编号为 node),子线段 l 到 r 上的数据组
GRP query(int std_l, int std_r, int node, int l, int r){
if (l == std_l && r == std_r){ //特殊子问题:要查询的子线段恰好为当前标准线段
return pair<pair<int, int>, pair<int, int> >(pair<int, int>(max1[node], max2[node]), pair<int, int>(cnt1[node], cnt2[node]));
}
int mid = (std_l + std_r) >> 1;
//要查询的子线段整体在中点左侧
if (r <= mid) return query(std_l, mid, node << 1, l, r);//要查询的子线段整体在中点左侧
//要查询的子线段整体在中点右侧
if (l >= mid + 1) return query(mid + 1, std_r, (node << 1) + 1, l, r);
//子线段横跨中点,将其拆分分别获取左侧部分的答案和右侧部分的答案,整合出当前段答案
GRP groupl = query(std_l, mid, node << 1, l, mid);
GRP groupr = query(mid + 1, std_r, (node << 1) + 1, mid + 1, r);
int lmax1 = groupl.first.first, rmax1 = groupr.first.first;
int lcnt1 = groupl.second.first, rcnt1 = groupr.second.first;
int lmax2 = groupl.first.second, rmax2 = groupr.first.second;
int lcnt2 = groupl.second.second, rcnt2 = groupr.second.second;
int value1, value2, count1, count2;
int con = compare(lmax1, lcnt1, rmax1, rcnt1, value1, count1);
if (con == 1) compare(lmax2, lcnt2, rmax1, rcnt1, value2, count2);
if (con == 2) compare(lmax1, lcnt1, rmax2, rcnt2, value2, count2);
if (con == 0) compare(lmax2, lcnt2, rmax2, rcnt2, value2, count2);
return pair<pair<int, int>, pair<int, int> >(pair<int, int>(value1, value2), pair<int, int>(count1, count2));
}
//从标准线段 std_l, std_r 向下继续进入更小的标准线段,寻找到 p 号元素,将其修改为 x,并在返回途中沿途修改标准线段上答案
void modify(int std_l, int std_r, int node, int p, int x){
if (std_l == std_r){
max1[node] = x, max2[node] = x;
return;
}
int mid = (std_l + std_r) >> 1;
if (p <= mid) modify(std_l, mid, node << 1, p, x);
else modify(mid + 1, std_r, (node << 1) + 1, p, x);
int lmax1 = max1[node << 1], rmax1 = max1[(node << 1) + 1];
int lmax2 = max2[node << 1], rmax2 = max2[(node << 1) + 1];
int con = compare(lmax1, cnt1[node << 1], rmax1, cnt1[(node << 1) + 1], max1[node], cnt1[node]);
if (con == 1) compare(lmax2, cnt2[node << 1], rmax1, cnt1[(node << 1) + 1], max2[node], cnt2[node]);
if (con == 2) compare(lmax1, cnt1[node << 1], rmax2, cnt2[(node << 1) + 1], max2[node], cnt2[node]);
if (con == 0) compare(lmax2, cnt2[node << 1], rmax2, cnt2[(node << 1) + 1], max2[node], cnt2[node]);
}
int main(){
cin >> n >> Q;
for (int i = 1; i<=n; i++){
cin >> a[i];
}
build(1, n, 1); //建树:线段左端点为 1,右端点为 n,结点编号为 1
for (int i = 1; i<=Q; i++){
int type, p, x;
cin >> type >> p >> x;
if (type == 1){ //将 a[p] 的值修改为 x
modify(1, n, 1, p, x);
}
if (type == 2){ //求从第 l 个数到第 r 个数这一部分线段上,第二大的值出现的次数
cout << query(1, n, 1, p, x).second.second << endl;
}
}
return 0;
}