题解:CF1838D Bracket Walk
审核题解,管理员辛苦了。
点击跳转题意
先考虑一些特殊情况。
:::info[特殊情况] 第一位是右括号,最后一位是左括号(废话),长度为奇数都不合法。
详细证明一下,显然一个合法括号序长度必须是偶数,但是我们无论怎么绕多加的括号数都是偶数(来回走嘛),凑不出偶数。 :::
然后考虑重复走一大段可以拆分成反复走一小段。
那么哪些段重复走是有用的,形如 )(,(),是没有用的,只有连续右括号或连续左括号是有用的。
(下面的分讨都是在特判完特殊情况后进行的)
考虑如果根本没有连续段,一定合法。
考虑只有一种连续段,一定不合法。
考虑两种连续段都有,除非第一个是右括号连续段,最后一个是左括号连续段才能合法。
然后就考虑维护每个连续括号序,直接上 set 即可。
:::info[详细说明] 我们开两个 set 分别存储连续左括号与连续右括号。 对于每一个点,我们只考虑它与后面点的贡献。 修改时去更新这个点前后所产生的贡献,直接做即可。 :::
#include <bits/stdc++.h>
using namespace std;
int n;
set <int> s1,s2;
char a[200005];
void sqqrt(int i,int op)
{
if(a[i]==a[i+1] && a[i]=='(')
{
if(op==1)
s1.insert(i);
else
s1.erase(i);
}
if(a[i]==a[i+1] && a[i]==')')
{
if(op==1)
s2.insert(i);
else
s2.erase(i);
}
}
void update(int i)
{
if(i!=1)
sqqrt(i-1,0);
if(i!=n)
sqqrt(i,0);
if(a[i]=='(')
a[i]=')';
else
a[i]='(';
if(i!=1)
sqqrt(i-1,1);
if(i!=n)
sqqrt(i,1);
}
bool query()
{
if(a[1]==')' || a[n]=='(')
return 0;
if(s1.empty() && s2.empty())
return 1;
else if(s1.empty() || s2.empty())
return 0;
auto i=s1.begin();
auto j=s2.begin();
if(*i>*j) return 0;
i=s1.end();
j=s2.end();
i--,j--;
if(*i>*j) return 0;
return 1;
}
signed main()
{
int q;
cin>>n>>q;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<n;i++)
sqqrt(i,1);
while(q--)
{
int id;
cin>>id;
if(n%2==1)
{
cout<<"NO"<<endl;
continue;
}
update(id);
if(query())
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
}
看到这里,就点个赞吧。