gym102889J线段树维护最大最小前缀和判断合法括号序列
Posted hesorchen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了gym102889J线段树维护最大最小前缀和判断合法括号序列相关的知识,希望对你有一定的参考价值。
题目
给出一个括号序列,每次操作给出一个区间 [ L : R ] [L:R] [L:R],将区间内括号取反,并回答操作后整个括号序列还是否合法。
解题思路
将(
看做1 ,)
看做-1。得到新的数组
a
i
a_i
ai
一个括号序列合法的判断条件是在任意位置,a数组的前缀和为非负数,并且数组和为0。
线段树维护区间前缀最小值核心代码:
tr[k].pre_min = min(tr[LSON].sum + tr[RSON].pre_min, tr[LSON].pre_min);
AC代码
#include <bits/stdc++.h>
using namespace std;
char a[100010];
struct node
{
int k, l, r, pre_min, pre_max, sum, lazy;
} tr[400010];
void pushup(int k)
{
tr[k].pre_min = min(tr[k * 2].sum + tr[k * 2 + 1].pre_min, tr[k * 2].pre_min);
tr[k].pre_max = max(tr[k * 2].sum + tr[k * 2 + 1].pre_max, tr[k * 2].pre_max);
tr[k].sum = tr[k * 2].sum + tr[k * 2 + 1].sum;
}
void pushdown(int k)
{
if (tr[k].l == tr[k].r)
return;
if (tr[k].lazy == -1)
{
tr[k].lazy *= -1;
tr[k * 2].lazy *= -1;
tr[k * 2].sum *= -1;
int temp = tr[k * 2].pre_max;
tr[k * 2].pre_max = -tr[k * 2].pre_min;
tr[k * 2].pre_min = -temp;
tr[k * 2 + 1].lazy *= -1;
tr[k * 2 + 1].sum *= -1;
temp = tr[k * 2 + 1].pre_max;
tr[k * 2 + 1].pre_max = -tr[k * 2 + 1].pre_min;
tr[k * 2 + 1].pre_min = -temp;
}
}
void build(int k, int l, int r)
{
tr[k].l = l;
tr[k].r = r;
tr[k].lazy = 1;
if (l == r)
{
tr[k].pre_min = tr[k].pre_max = a[l] == '(' ? 1 : -1;
tr[k].sum = a[l] == '(' ? 1 : -1;
return;
}
int mid = l + r >> 1;
build(k * 2, l, mid);
build(k * 2 + 1, mid + 1, r);
pushup(k);
}
void change(int k, int L, int R)
{
if (tr[k].l == L && tr[k].r == R)
{
tr[k].lazy *= -1;
tr[k].sum *= -1;
int temp = tr[k].pre_max;
tr[k].pre_max = -tr[k].pre_min;
tr[k].pre_min = -temp;
return;
}
if (tr[k].lazy == -1)
pushdown(k);
int mid = tr[k].l + tr[k].r >> 1;
if (R <= mid)
change(k * 2, L, R);
else if (L > mid)
change(k * 2 + 1, L, R);
else
{
change(k * 2, L, mid);
change(k * 2 + 1, mid + 1, R);
}
pushup(k);
}
int query1(int k, int L, int R)
{
if (tr[k].l == L && tr[k].r == R)
return tr[k].pre_min;
if (tr[k].lazy == -1)
pushdown(k);
int mid = tr[k].l + tr[k].r >> 1;
if (R <= mid)
return query1(k * 2, L, R);
else if (L > mid)
return query1(k * 2 + 1, L, R);
else
return min(query1(k * 2, L, mid), query1(k * 2 + 1, mid + 1, R));
}
int query2(int k, int L, int R)
{
if (tr[k].l == L && tr[k].r == R)
return tr[k].sum;
if (tr[k].lazy == -1)
pushdown(k);
int mid = tr[k].l + tr[k].r >> 1;
if (R <= mid)
return query2(k * 2, L, R);
else if (L > mid)
return query2(k * 2 + 1, L, R);
else
return query2(k * 2, L, mid) + query2(k * 2 + 1, mid + 1, R);
}
int main()
{
int n, m;
scanf("%d %d", &n, &m);
scanf("%s", a + 1);
build(1, 1, n);
while (m--)
{
int l, r;
scanf("%d %d", &l, &r);
change(1, l, r);
if (tr[1].sum == 0 && tr[1].pre_min == 0)
puts("YES");
else
puts("NO");
}
return 0;
}
以上是关于gym102889J线段树维护最大最小前缀和判断合法括号序列的主要内容,如果未能解决你的问题,请参考以下文章