当前位置: 代码迷 >> 综合 >> UVALive 6838 Flipping Parentheses(线段树、单点更新、区间查询)
  详细解决方案

UVALive 6838 Flipping Parentheses(线段树、单点更新、区间查询)

热度:66   发布时间:2023-12-08 11:00:49.0

题目链接:
UVALive 6838 Flipping Parentheses
题意:
给出一个长度为n个串,每个字符只能是’(‘或’)’,而且左括号和右括号个数相等,在操作的过程中,要保证这个串的任意前缀串的左括号个数都要大于等于右括号的个数。
有Q个操作。每次操作输入一个坐标t将(坐标从1~n)这个坐标下的括号取反,即左括号变右括号,右括号变左括号。
对每次输入输出最靠前的一个坐标,使得改变这个坐标下的字符(取反),仍然满足任意前缀子串的左括号个数大于等于右括号个数?
分析:
将左括号个数减去右括号个数设为平衡度。显然整个串的平衡度为0,而且任意前缀串的平衡度大于等于0.
假设将id位置的括号从左变成右,那么从这个位置一直到串尾的每个位置上的前缀串的平衡度都增加2,相反,如果由右括号变成左括号,那么从这个位置一直到串尾的每个位置上的前缀串的平衡度都减少2.
首先考虑将第t位置的括号由左变右的情况,。显然需要找到最靠前的右括号将其变为左括号,假设坐标为id,如何找到id呢?可以在线段树中保存当前区间的右括号的个数,那么就相当于单点查询了。找到id后还需注意,因为我们需要的是操作完成后最靠前的右括号,如果id>t,那么因为需要将t位置的左括号改为右括号,那么这是id应该就是t,先变成右括号,再变成左括号。然后就是区间更新,更新从id位置到串尾的线段树区间最小前缀平衡度。
为什么要保存区间最小前缀平衡度呢?是因为接下来将第t位置的括号由右变左需要用到。我们需要找到的是一个左括号,同样假设下标id,将其变为右括号,而且保持任意前缀
子串的平衡度非负,这是我们应该先更新在查找。如果从前往后找的话,那么从id以后的子串的前缀平衡度就难以保证了。所以我们需要从后往前找。
找到一个这样的最靠前的位置:从这个位置往后(包括这个位置)的前缀平衡度恒大于等于2,因为左括号变右相当于平衡度减2.也就是从后往前找到第一个前缀平衡度小于2的后一个位置就是我们要随着t改变的位置。

更新区间前缀平衡度最小值要用lazy标记。
还有一个特例需要注意。就是要改变的位置是第一个括号和最后一个括号时,只能是在把它变回来。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <climits>
#include <cmath>
#define lson(x) (x<<1)
#define rson(x) ((x<<1)|1)
using namespace std;
const int MAX_N=300010;int n,Q;
char s[MAX_N];
int sum[MAX_N];struct Segtree{int left,right;int num1,num2,balance_min,add;
}segtree[MAX_N<<2];inline void push_up(int cur)
{segtree[cur].num1=segtree[lson(cur)]
  相关解决方案