比赛链接
本题题意:给你一个合法的括号串,比如(())()()()()
然后会改变q次,每次改变一个括号的朝向,然后你要输出一个数ans表示你要翻转第ans个括号的朝向使得新的串仍然是合法的;同时如果存在多个ans,请对最左边的括号操作。即要求找一个leftest 下标翻转后能保持括号信息正常;
括号匹配容易想到栈和前缀和。栈排除,因为有q次操作;前缀和设左括号为+1,右括号为-1,于是能得到一个前缀和串;
容易知道,括号串合法的充要条件是前缀和串处处不为负数;
又双叒叕容易想到:
如果翻转了pos位置的一个左括号,也就是+1变成了-1,也就是说相当于Pre[pos,N]每一个数都-2。然后我们就得翻转一个Loc<=pos,使得Loc为右括号,被你翻转成了左括号,于是相当于[Loc,pos-1]的数+2,所以自然而然的,Loc就要求是最左边的一个右括号即可,用一个set维护吧那就。
如果翻转了pos位置的一个右括号,也就是-1变成了+1,那同样的找一个Loc位置为左括号的下标,翻转为右括号,如此相当于[Loc,pos-1]-2,如此得到的就是最左边的Loc,使得Loc到pos间的前缀和都是>=2的;于是答案就出来了:二分左端点,而且最重要的是最左边的连续的>=2的点一定是左括号,因为Loc-1的前缀和一定是<2的,即Loc位置比Loc-1位置多了1,于是Loc一定是左括号。
前缀和维护的自然就是前缀和数组的最小值了,当最小值>=2说明整个区间的数都>=2;
于是二分左端点时间log,线段树也log,此题
N
l
o
g
2
Nlog^2
Nlog2做完;
Code:
#include <bits/stdc++.h>
const double eps = 1e-6;
const double PI = acos(-1);
const int INF = 0x3f3f3f3f;
#define ms(a,k) memset(a,k,sizeof(a))
#define X first
#define Y second
#define pii pair<int ,int >
#define lowbit(a) a&(-a)
typedef long long ll ;
typedef unsigned long long ull;
using namespace std;
inline void read(int &x){scanf("%d",&x);}
const int mod = 998244353;
const int maxn = 300000+10;
int tr[maxn*4],lazy[maxn*4];//tr维护的是区间最小值;
set<int >loc;
char s[maxn];
int pre[maxn];
void build(int k,int l,int r)
{
tr[k] = INF;
if(l==r){tr[k] = pre[l];return ;}
int mid = l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
tr[k] = min(tr[k<<1] , tr[k<<1|1]);
}
void down(int k,int l,int r)
{//lazy是区间加
int mid = l+r>>1;
lazy[k<<1] += lazy[k];
lazy[k<<1|1] += lazy[k];
tr[k<<1] += lazy[k];
tr[k<<1|1] += lazy[k];
lazy[k] = 0;
}
void add(int k,int l,int r,int L,int R,int v)
{
if(L>R)return ;
if(L<=l&&r<=R){tr[k]+=v;lazy[k]+=v;return ;}
int mid = l+r>>1;
down(k,l,r);
if(L<=mid)add(k<<1,l,mid,L,R,v);
if(R>=mid+1)add(k<<1|1,mid+1,r,L,R,v);
tr[k] = min(tr[k<<1],tr[k<<1|1]);
}
int query(int k,int l,int r,int L,int R)
{
//printf("%d %d %d %d %d\n",k,l,r,L,R);
if(L<=l&&r<=R){return tr[k];}
int mid = l+r>>1;
down(k,l,r);
int res =INF;
if(L<=mid){res = min(res, query(k<<1,l,mid,L,R)) ;}
if(mid+1<=R){res = min(res, query(k<<1|1,mid+1,r,L,R)) ;}
return res;
}
int main()
{
int n,q;read(n);read(q);
scanf("%s",s+1);
for(int i =1;i<=n;++i)
{
if(s[i]=='('){pre[i] = pre[i-1]+1; }
else {pre[i] = pre[i-1]-1;loc.insert(i);}
}
build(1,1,n);
while(q--)
{
int pos ;read(pos);
if(s[pos] == '(' )
{//找到最左边的右括号翻转成为左括号即可;
loc.insert(pos);
int tar = *loc.begin() ;
loc.erase(tar);
s[pos] = ')';
s[tar] = '(';
add(1,1,n,tar,pos-1,2);
printf("%d\n",tar);
}
else
{//找到一个最左边的tar满足本身为左括号->右括号,且满足min[tar, pos-1]>=2;
int l = 0,r = pos;
while(l+1<r)//r作为答案
{
int mid = l+r>>1;
if( query(1,1,n,mid,pos-1) >= 2 )r = mid;
else l = mid;
}
int tar = r;//如果连r都不是答案,那么答案只能是pos了
if(tar == pos){printf("%d\n",tar);continue;}
loc.erase(pos);
loc.insert(tar);
s[pos] = '(';
s[tar] = ')';
add(1,1,n,tar,pos-1,-2);
printf("%d\n",tar);
}
//printf("%s\n",s+1);
}
return 0;
}