bzoj 2209: [Jsoi2011]括号序列(splay)

来源:互联网 发布:linux 组播编程 编辑:程序博客网 时间:2024/05/18 02:47

2209: [Jsoi2011]括号序列

Time Limit: 20 Sec  Memory Limit: 259 MB
Submit: 1082  Solved: 526
[Submit][Status][Discuss]

Description

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

Sample Output

2
2
0

HINT

100%的数据满足N,Q不超过10^5

Source

第一轮

[Submit][Status][Discuss]


题解: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 500003using 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); } }}


0 0