Parenthesis Checking 括号检查 (线段树)

原题F - Parenthesis Checking (atcoder.jp)
题目描述

我们定义一个正确的括号序列是一个满足以下条件之一的字符串。

  • 它是一个空字符串。
  • 它是一串字符的连接,按照顺序是(、A、),其中A是一个正确的括号序列。
  • 它是一串字符的连接,按照顺序是A、B,其中A和B都是正确的括号序列。

我们有一个长度为N的字符串S,由()组成。

给定Q个查询Query1, Query2, ..., QueryQ,按顺序处理它们。有两种类型的查询,具体格式和内容如下:

  • 1 l r:交换S中第l个和第r个字符。
  • 2 l r:确定从第l个到第r个字符的连续子串是否是一个正确的括号序列。
输入描述

测试数据第一行输入两个整数N、Q
第二行输入字符串S
接下来Q行,每行一个查询。

数据范围:

  • 1≤N,Q≤2×10e5
  • 1≤l<r≤N
输出描述

对于格式为2 l r的每个查询,如果连续子串是一个正确的括号序列,则打印Yes,否则打印No,然后换行。

测试样例

测试样例1

5 3
(())(
2 1 4
2 1 2
2 4 5
Yes
No
No

测试样例2

5 3
(())(
2 1 4
1 1 4
2 1 4
Yes
No

测试样例3

8 8
(()(()))
2 2 7
2 2 8
1 2 5
2 3 4
1 3 4
1 3 5
1 1 4
1 6 8
Yes
No
No
提示

样例一解释:

在第一个查询中,(()) 是一个正确的括号序列。

在第二个查询中,(( 不是一个正确的括号序列。

在第三个查询中,)( 也不是一个正确的括号序列。

思路:

这是一个线段树匹配和修改问题,由题可知,左右括号在匹配完和未匹配完前一定保证左括号数量大于 右括号数量,所以我们可以根据每个段的左右括号数量和来判断是否匹配

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const ll N = 2e5 + 10;
const ll inf = 0x3f3f3f3f3f3f;
ll n, m, ans, num;
string s;
ll arr[N];
struct node {
    ll l, r, val;
    ll minn;
} a[N << 2];

void build(ll node, ll l, ll r) {//建树
    a[node].l = l;//每个节点的左右范围
    a[node].r = r;
    a[node].minn = inf;//判段该节点左右括号的和最小值,初始为最大值
    if (l == r) {
        a[node].val = arr[l];//左括号为1,右括号为-1
        a[node].minn = arr[l];
        return;
    }
    ll mid = (l + r) >> 1;
    build(node << 1, l, mid);
    build(node << 1 | 1, mid + 1, r);
    a[node].val = a[node << 1].val + a[node << 1 | 1].val;
    a[node].minn = min(a[node << 1].minn, a[node << 1].val + a[node << 1 | 1].minn);
}

void change(ll node, ll l, ll r, ll val) {
    if (a[node].l == l && a[node].r == r) {//单点修改
        a[node].val = val;
        a[node].minn = val;
        return;
    }
    ll mid = (a[node].l + a[node].r) >> 1;
    if (l <= mid)change(node << 1, l, r, val);
    if (r > mid)change(node << 1 | 1, l, r, val);
    a[node].val = a[node << 1].val + a[node << 1 | 1].val;
    a[node].minn = min(a[node << 1].minn, a[node << 1].val + a[node << 1 | 1].minn);
}

pair<ll, ll> look(ll node, ll l, ll r) {//查询
    if (a[node].l >= l && a[node].r <= r) {
        return {a[node].val, a[node].minn};
    }
    pair<ll, ll> ls, rs;
    ll mid = (a[node].l + a[node].r) >> 1;
    if (l <= mid)ls = look(node << 1, l, r);
    if (r > mid)rs = look(node << 1 | 1, l, r);
    return {ls.first + rs.first, min(ls.second, ls.first + rs.second)};
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    cin >> s;
    s = " " + s;
    for (ll i = 1; i <= n; i++) {
        if (s[i] == '(')arr[i] = 1;
        else arr[i] = -1;
    }
    build(1, 1, n);
    while (m--) {
        ll x, y, op;
        cin >> op >> x >> y;
        if (op == 1) {
            swap(s[x], s[y]);
            num = 1;
            if (s[x] == ')')num = -1;
            change(1, x, x, num);
            num = 1;
            if (s[y] == ')')num = -1;
            change(1, y, y, num);

        } else {
            pair<ll, ll> res = look(1, x, y);
            if (res.first == 0 && res.second == 0)cout << "Yes" << endl;//若res.first!=0说明左括号多了,res.second多了说明最小值小于0,即右括号多了
            else cout << "No" << endl;
        }

    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值