AtCoder_abc_D 线段树求调
  • 板块题目总版
  • 楼主1CR_wait_ME
  • 当前回复2
  • 已保存回复2
  • 发布时间2024/3/4 17:47
  • 上次更新2024/3/4 20:14:54
查看原帖
AtCoder_abc_D 线段树求调
893971
1CR_wait_ME楼主2024/3/4 17:47

[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;
}
2024/3/4 17:47
加载中...