题目链接:传送门
A string consisting only of parentheses ‘(’ and ‘)’ is called balanced if it is one of the following.
Note that the condition is stronger than merely the numbers of ‘(’ and ‘)’ are equal. For instance, “())(()” is not balanced.
Your task is to keep a string in a balanced state, under a severe condition in which a cosmic ray may flip the direction of parentheses.
You are initially given a balanced string. Each time the direction of a single parenthesis is flipped, your program is notified the position of the changed character in the string. Then, calculate and output the leftmost position that, if the parenthesis there is flipped, the whole string gets back to the balanced state. After the string is balanced by changing the parenthesis indicated by your program, next cosmic ray flips another parenthesis, and the steps are repeated several times.
The input consists of a single test case formatted as follows.
The first line consists of two integers N and Q (2 ≤ N ≤ 300000, 1 ≤ Q ≤ 150000). The second line is a string s of balanced parentheses with length N. Each of the following Q lines is an integer qi (1 ≤ qi ≤ N) that indicates that the direction of the qi-th parenthesis is flipped.
For each event qi, output the position of the leftmost parenthesis you need to flip in order to get back to the balanced state.
Note that each input flipping event qi is applied to the string after the previous flip qi − 1 and its fix.
6 3 ((())) 4 3 1 20 9 ()((((()))))()()()() 15 20 13 5 3 10 3 17 18
2 2 1 2 20 8 5 3 2 2 3 18
In the first sample, the initial state is “((()))”. The 4th parenthesis is flipped and the string becomes “(((())”. Then, to keep the balance you should flip the 2nd parenthesis and get “()(())”. The next flip of the 3rd parenthesis is applied to the last state and yields “())())”. To rebalance it, you have to change the 2nd parenthesis again yielding “(()())”.
Asia Regional Contest, Tokyo, 2014
题目大意:
给定一个长度为n的匹配括号串。翻转其中一个括号,问我们应该翻转该括号最左边的哪个括号,能使该括号串重新匹配。翻转多次。
解题思路:
我们先将字符串处理成一个前缀和的数组,'('则+1,')'则-1。当我们最后翻转某一对括号时,怎么表示这种变化?其实就是前缀和数组在某一区间内+2或-2,容易想到用线段树对其进行维护。
当我们翻转某一个括号时,这里分两种情况讨论:
1.'(' ----> ')',假设左括号下标是L,那么我们需要在区间[1,L-1]中找到一个a,使区间[a,L-1]的最小值M>=2,区间[1,a-1]的最小值M < 2,此时答案ans = a,假如不存在这样一个a,那么ans = L。为什么是这样呢,因为要想使括号串最后匹配,我们只要满足前缀和数组的每个数都不小于0即可。而我们将一对括号翻转后,实际上会使某一区间内的所有数-2。所以上面的结论是就正确的。那么我们可以这样处理,用线段树记录区间最小值,然后在区间[1,L-1]内二分。
2.')' ----> '(',这种情况比上面的情况简单。因为我们只要使[a,L-1]的最小值M>=0,这是明显成立的。所以我们只要从左边开始找到第一个')'即可。假如该下标为R,R > L,ans = L,否则ans = R。我们可以用一个set记录所有')'的下标。
代码:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define lchild (rt<<1)
#define rchild (rt<<1|1)
typedef long long llt;
const int N = 300010;
const int M = 50010;
const int INF = 0x3fffffff;
int A[N],Add[N<<2],Min[N<<2];
char st[N];
setSet; //记录右括号
void init()
{
memset(Add,0,sizeof(Add));
}
void PushUp(int rt)
{
Min[rt] = min(Min[lchild],Min[rchild]);
}
void Build(int l,int r,int rt)
{
if(l == r){
Min[rt] = A[l];
return;
}
int m = (l+r)>>1;
Build(l,m,lchild);
Build(m+1,r,rchild);
PushUp(rt);
}
void PushDown(int rt)
{
if(Add[rt]){
Add[lchild] += Add[rt];
Add[rchild] += Add[rt];
Min[lchild] += Add[rt];
Min[rchild] += Add[rt];
Add[rt] = 0;
}
}
void Update(int L,int R,int C,int l,int r,int rt)
{
if(L <= l && r <= R){
Min[rt] += C;
Add[rt] += C;
return;
}
int m = (l+r)>>1;
PushDown(rt);
if(L <= m) Update(L,R,C,l,m,lchild);
if(R > m) Update(L,R,C,m+1,r,rchild);
PushUp(rt);
}
int Query(int L,int R,int l,int r,int rt)
{
if(L <= l && r <= R){
return Min[rt];
}
int m = (l+r)>>1;
PushDown(rt);
int ans = INF;
if(L <= m) ans = min(ans,Query(L,R,l,m,lchild));
if(R > m) ans = min(ans,Query(L,R,m+1,r,rchild));
return ans;
}
int solve(int a,int n,int val)
{
int l = 1,r = a-1,mid,ans = a;
while(l <= r){
mid = (l+r)>>1;
// cout << l << " " << r << endl;
if(Query(mid,a-1,1,n,1) >= val){
ans = mid;
r = mid-1;
}else{
l = mid+1;
}
}
return ans;
}
int main()
{
// freopen("D:\\00.in","r",stdin);
// freopen("D:\\data.txt"3,"w",stdout);
int n,m;
while(~scanf("%d%d",&n,&m)){
scanf(" %s",st);
A[0] = 0; Set.clear();
for(int i = 0; i < n; ++i){
if(st[i] == '(') A[i+1] = A[i]+1;
else{
A[i+1] = A[i]-1;
Set.insert(i+1);
}
}
init();
Build(1,n,1);
int a,ans;
for(int i = 0; i < m; ++i){
scanf("%d",&a);
if(st[a-1] == ')'){
ans = solve(a,n,2);
if(ans < a){
Update(ans,a-1,-2,1,n,1);
st[a-1] = '(',st[ans-1] = ')';
Set.insert(ans);
Set.erase(a);
}
}else{
ans = *Set.begin();
if(ans > a) ans = a;
if(ans < a){
Update(ans,a-1,2,1,n,1);
st[a-1] = ')',st[ans-1] = '(';
Set.insert(a);
Set.erase(ans);
}
}
printf("%d\n",ans);
}
}
return 0;
}