2209: [Jsoi2011]括号序列
Time Limit: 20 Sec Memory Limit: 259 MBSubmit: 1082 Solved: 526
[ Submit][ Status][ Discuss]
Description
![](http://www.lydsy.com/JudgeOnline/images/2209.jpg)
Input
输入数据的第一行包含两个整数N和Q,分别表示括号序列的长度,以及操作的个数。 第二行包含一个长度为N的括号序列。 接下来Q行,每行三个整数t、x和y,分别表示操作的类型、操作的开始位置和操作的结 束位置,输入数据保证x不小于y。其中t=0表示询问操作、t=1表示反转操作、t=2表示翻转操 作。
Output
对于每一个询问操作,输出一行,表示将括号序列的该子序列修改为配对,所需的最少改动 个数。
Sample Input
6 3
)(())(
0 1 6
0 1 4
0 3 4
)(())(
0 1 6
0 1 4
0 3 4
Sample Output
2
2
0
2
0
HINT
100%的数据满足N,Q不超过10^5
。
Source
题解:splay
我们将括号序列改成-1与1的序列,用-1表示(,用1表示)。
那么一段区间中前缀和的最大值就是未匹配的)的数量(prex表示),后缀和的最小值为未匹配的(的数量(sufn表示)。
如果未匹配的(,)都是偶数的话,那答案就是(prex+sufn)/2
如果未匹配的(,)都是奇数的话,那答案就是(prex+sufn)/2+1 (至于为什么,画一画括号找找规律啦)
翻转操作: 交换左右子树,打翻转标记,直接交换pre 和 suf 数组即可,因为以前的前缀变成了后缀,数值并未发生改变。
反转操作:打反转标记,将pren,prex,sufn,sufx,sum,key取反,swap(pren,prex),swap(sufn,sufx)。因为反转之后-1,与1,都会发生变化,而且取反后最大值与最小值都会发生大小交换。
有两个标记会不会发生标记冲突呢?这道题比较好,通过手动操作可以发现两个标记互不影响,先下放哪一个的效果都是相同的。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 500003
using namespace std;
int n,m,fa[N],size[N],sum[N],key[N],ch[N][3],prefx[N],prefn[N],sufx[N],sufn[N];
int pos[N],root,neg[N],rev[N],cnt;
char s[N];
void update(int now)//前缀和的最大值是多出的)的数量(最大值应该大于等于0),后缀和的最小值是多出的( 的数量(最小值应该小于等于0)
{
prefx[0]=sufx[0]=-1000000000;
prefn[0]=sufn[0]=1000000000; key[0]=0;
int l=ch[now][0]; int r=ch[now][1];
size[now]=size[l]+size[r]+1;
sum[now]=sum[l]+sum[r]+key[now];
int lenp=max(sum[l]+key[now],sum[l]+prefx[r]+key[now]);
prefx[now]=max(lenp,prefx[l]);
lenp=min(sum[l]+key[now],sum[l]+prefn[r]+key[now]);
prefn[now]=min(lenp,prefn[l]);
int lens=max(sum[r]+key[now],sum[r]+sufx[l]+key[now]);
sufx[now]=max(lens,sufx[r]);
lens=min(sum[r]+key[now],sum[r]+sufn[l]+key[now]);
sufn[now]=min(lens,sufn[r]);
}
void change(int now)
{
swap(prefx[now],sufx[now]);
swap(prefn[now],sufn[now]);
}
void change1(int now)
{
sum[now]=-sum[now]; key[now]=-key[now];
prefx[now]=-prefx[now]; sufx[now]=-sufx[now];
prefn[now]=-prefn[now]; sufn[now]=-sufn[now];
swap(prefx[now],prefn[now]);
swap(sufn[now],sufx[now]);
}
void pushdown(int x)
{
if (rev[x])
{
change(ch[x][0]); change(ch[x][1]);
swap(ch[x][0],ch[x][1]);
rev[ch[x][0]]^=1; rev[ch[x][1]]^=1;
rev[x]=0;
}
if (neg[x])
{
change1(ch[x][0]); change1(ch[x][1]);
neg[ch[x][0]]^=1; neg[ch[x][1]]^=1;
neg[x]=0;
}
}
int build(int l,int r,int f)
{
if (l>r) return 0;
int mid=(l+r)/2; int now=++cnt;
key[now]=pos[mid]; fa[now]=f;
ch[now][0]=build(l,mid-1,now);
ch[now][1]=build(mid+1,r,now);
update(now);
//cout<<l<<" "<<r<<" "<<now<<"!"<<endl;
// cout<<prefx[now]<<" "<<prefn[now]<<" "<<sufx[now]<<" "<<sufn[now]<<" "<<sum[now]<<endl;
return now;
}
int get(int x)
{
return ch[fa[x]][1]==x;
}
void rotate(int x)
{
int y=fa[x]; int z=fa[y];
pushdown(y); pushdown(x); int which=get(x);
ch[y][which]=ch[x][which^1]; fa[ch[x][which^1]]=y;
ch[x][which^1]=y; fa[y]=x; fa[x]=z;
if (z) ch[z][ch[z][1]==y]=x;
update(y); update(x);
}
void splay(int x,int tar)
{
for (int f;(f=fa[x])!=tar;rotate(x))
if (fa[f]!=tar)
rotate(get(x)==get(f)?f:x);
if (!tar) root=x;
}
int find(int x)
{
int now=root;
while (true)
{
pushdown(now);
if (x<=size[ch[now][0]])
now=ch[now][0];
else
{
int ans=size[ch[now][0]]+1;
if (ans==x) return now;
x-=ans; now=ch[now][1];
}
}
}
int main()
{
freopen("a.in","r",stdin);
freopen("my.out","w",stdout);
scanf("%d%d\n",&n,&m);
scanf("%s",s+1);
for (int i=1;i<=n;i++)
if (s[i]=='(') pos[i]=-1;
else pos[i]=1;
root=build(0,n+1,0);
for (int i=1;i<=m;i++)
{
int opt,x,y;
scanf("%d%d%d",&opt,&x,&y);
int l=find(x); int r=find(y+2);
//cout<<l<<" "<<r<<endl;
splay(l,0); splay(r,root);
int t=ch[ch[root][1]][0];
if (opt==0)
{
int ans=prefx[t]; int ans1=sufn[t];
if (ans1>0) ans1=0;
else ans1=-ans1;
if (ans<0) ans=0;
if (ans%2||ans1%2) printf("%d\n",(ans+ans1)/2+1);
else printf("%d\n",(ans+ans1)/2);
}
if (opt==1)
{
neg[t]^=1;
change1(t); update(ch[root][1]); update(root);
}
if (opt==2)
{
rev[t]^=1;
change(t); update(ch[root][1]); update(root);
}
}
}