CSU - 1809 (前缀和)

Parenthesis

Time Limit: 5 Sec Memory Limit: 128 Mb Submitted: 4132 Solved: 1026

Description

Bobo has a balanced parenthesis sequence P=p1 p2…pn of length n and q questions.
The i-th question is whether P remains balanced after pai and pbi swapped. Note that questions are individual so that they have no affect on others.

Parenthesis sequence S is balanced if and only if:
1. S is empty;
2. or there exists balanced parenthesis sequence A,B such that S=AB;
3. or there exists balanced parenthesis sequence S’ such that S=(S’).

Input

The input contains at most 30 sets. For each set:
The first line contains two integers n,q (2≤n≤105,1≤q≤105).
The second line contains n characters p1 p2…pn.
The i-th of the last q lines contains 2 integers ai,bi (1≤ai,bi≤n,ai≠bi).

Output

For each question, output “Yes” if P remains balanced, or “No” otherwise.

Sample Input

4 2
(())
1 3
2 3
2 1
()
1 2

Sample Output

No
Yes
No

Hint

Source

湖南省第十二届大学生计算机程序设计竞赛

题目大意

首先定义 平衡序列 ,即:

  1. 空序列为平衡序列;
  2. AB 为平衡序列,则 (A) 是平衡序列, AB 也是平衡序列;

那么我们发现平衡序列实质上就是指每一个 ) 均与左边的某个 ( 唯一匹配的括号序列。
现给出一个平衡序列,再给出 q 对查询:求解将位置为 ab 的括号调换之后是该序列否依然是平衡序列。

思路

如果一个序列是平衡序列,那么显然对序列任意位置都满足:从序列开始到该位置的所有 ( 数量大于等于 ) 数量,否则必然有 ) 找不到匹配。既然如此,不妨用 前缀和 记录 () 的分布情况,即令 ( 的贡献值为 1) 的贡献值为 -1 ,这样前缀和就记录了每个位置及其左边() 多出来的数量,记该前缀和数组为 pre[]

接下来再来分析询问, ab 的关系实质上可以分为如下几种情况:

  1. ab 位置的括号相同, 即均为 () , 那么显然序列的平衡性不会改变;
  2. 若情况1不成立,则考察 ab 的大小关系, 进行交换使得 a < b 成立;
  3. a 位置为 )b 位置为 ( ,那么显然, 因为 a 位置的括号往右移动,仍然可以和之前匹配的 ( 匹配。 b 位置同理;
  4. a 位置为 (b 位置为 ) ,此次交换会且仅会改变从 a 位置到 b - 1 位置的前缀和数组 pre[] ——因为对该段上的每一个元素, 其左边(包括本身)少了一个 ( 而多了一个 ) 即前缀和要减去 2。为了使该段上的每个前缀和仍然满足 pre[i] >= 0 就必须保证之前的所有 pre[i] 满足 pre[i] >= 2 。 由于 q 的规模很大, 当然不能在线查询 ab - 1 之间所有 pre[i] 的值。由于我太菜而学不会 RMQ ,也懒得使用码量巨大的 线段树 来维护,所以可以使用第二个前缀和数组 preLes2[] 来维护 pre[i] < 2 的数量, 使用时检查 preLes2[b - 1] - preLes2[a - 1] <= 0 是否成立, 若成立则代表该段上没有 pre[i] < 2 的元素, 也就代表交换 ab 后该序列依然为平衡序列;

AC代码

#include <bits/stdc++.h>
using namespace std;

inline int read()
{
    char ch;
    bool isNeg = false;
    int a = 0;
    while(!((((ch = getchar()) >= '0') && (ch <= '9')) || (ch == '-')));
    if(ch != '-')
    {
        a *= 10;
        a += ch - '0';
    }
    else
    {
        isNeg = true;
    }
    while(((ch = getchar()) >= '0') && (ch <= '9'))
    {
        a *= 10;
        a += ch - '0';
    }
    if(isNeg)
    {
        a = -a;
    }
    return a;
}

inline void write(int a)
{
    if(a < 0)
    {
        putchar('-');
        a = -a;
    }
    if(a >= 10)
    {
        write(a / 10);
    }
    putchar(a % 10 + '0');
}

const int MAXN = 1e5 + 5;

inline int getVal(char c)
{
    return c == '(' ? 1 : -1;
}

int main()
{
    int n, q;
    while (~scanf("%d %d", &n, &q))
    {
        static char seq[MAXN];
        scanf("%s", seq + 1);

        static int pre[MAXN], preLes2[MAXN];

        for (int i = 1; seq[i] != '\0'; i++)
            pre[i] = pre[i - 1] + getVal(seq[i]),
            preLes2[i] = preLes2[i - 1] + (pre[i] < 2);

        for (int i = 1; i <= q; i++)
        {
            int a = read(), b = read();
            if (seq[a] == seq[b]) puts("Yes");
            else
            {
                if (a > b) swap(a, b);
                if (seq[a] == ')' || preLes2[b - 1] - preLes2[a - 1] <= 0)
                    puts("Yes");
                else puts("No");
            }
        }
    }
}

TIPS

  • 序列的位置从 1 开始计数;
  • 询问中没有保证 ab 之间的大小关系;

总结

关键要找出平衡序列的本质。激励我学习 RMQ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值