2209: [Jsoi2011]括号序列
Time Limit: 20 Sec Memory Limit: 259 MBSubmit: 1404 Solved: 699
[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
)(())(
0 1 6
0 1 4
0 3 4
Sample Output
2
2
0
2
0
HINT
100%的数据满足N,Q不超过10^5
。
Source
首先,对于一个括号序列,例如:())()(((,我们把可以匹配的去掉,就变成了:)(((。换句话说,对于一般的括号序列,化简以后就变成了左边x个")",右边y个"("。显然(x+y)为偶数,我们可以发现此时答案为x/2+y/2(x,y为偶数)或者x/2+1+y/2+1(x,y为奇数),合并一下就是[(x+1)/2]+[(y+1)/2]。
关键是对于序列(l,r),x和y怎么求。实际上我们发现x就是求左端点为l,右端点<=r时,序列中右括号比左括号多的个数的最大值(>=0)。换句话说,如果令"("=1",("=-1,实际上x就是最小左子段和,y就是最大右子段和。由于还有反转(不是翻转)操作,因此还需要维护最大左子段和和最小右子段和。为了维护最小最大子段和,还需要维护一个区间和。
然后就可以用splay的经典提取操作了。打两个标记就好了。
因为在find的过程中已经翻转保证了当前根那部分是正确就可以了。
1 #pragma GCC optimize(2) 2 #pragma G++ optimize(2) 3 #include<iostream> 4 #include<algorithm> 5 #include<cmath> 6 #include<cstdio> 7 #include<cstring> 8 9 #define N 100007 10 using namespace std; 11 inline int read() 12 { 13 int x=0,f=1;char ch=getchar(); 14 while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} 15 while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} 16 return x*f; 17 } 18 19 int n,m,rt; 20 int c[N][2],sum[N],a[N],sz[N],fa[N]; 21 bool rev[N],ops[N]; 22 char ch[N]; 23 struct Node 24 { 25 int l0,l1,r0,r1; 26 void LDI() 27 { 28 l0=-l0,l1=-l1; 29 r0=-r0,r1=-r1; 30 } 31 }val[N]; 32 33 34 void update(int p) 35 { 36 int l=c[p][0],r=c[p][1]; 37 sum[p]=a[p]+sum[l]+sum[r],sz[p]=sz[l]+sz[r]+1; 38 val[p].l0=min(val[l].l0,sum[l]+a[p]+val[r].l0); 39 val[p].l1=max(val[l].l1,sum[l]+a[p]+val[r].l1); 40 val[p].r0=min(val[r].r0,sum[r]+a[p]+val[l].r0); 41 val[p].r1=max(val[r].r1,sum[r]+a[p]+val[l].r1); 42 } 43 void rotate(int x,int &k) 44 { 45 int y=fa[x],z=fa[y],l,r; 46 if(c[y][0]==x)l=0;else l=1;r=l^1; 47 if(y==k)k=x; 48 else if(c[z][0]==y)c[z][0]=x; 49 else c[z][1]=x; 50 fa[x]=z,fa[y]=x,fa[c[x][r]]=y; 51 c[y][l]=c[x][r],c[x][r]=y; 52 update(y),update(x); 53 } 54 void splay(int x,int &k) 55 { 56 while(x!=k) 57 { 58 int y=fa[x],z=fa[y]; 59 if(y!=k) 60 { 61 if(c[y][0]==x^c[z][0]==y) rotate(x,k); 62 else rotate(y,k); 63 } 64 rotate(x,k); 65 } 66 } 67 void build(int &p,int l,int r,int par) 68 { 69 if(l>r){p=0;return;} 70 p=(l+r)>>1;fa[p]=par; 71 if(l==r) 72 { 73 sum[p]=a[l],sz[p]=1; 74 if(a[l]<0) val[p].l0=val[p].r0=-1; 75 else val[p].l1=val[p].r1=1; 76 return; 77 } 78 build(c[p][0],l,p-1,p),build(c[p][1],p+1,r,p); 79 update(p); 80 } 81 void rollback(int p) 82 { 83 ops[p]^=1,sum[p]=-sum[p],a[p]=-a[p]; 84 swap(val[p].l0,val[p].l1),swap(val[p].r0,val[p].r1); 85 val[p].LDI(); 86 } 87 void rever(int p) 88 { 89 rev[p]^=1; 90 swap(val[p].l0,val[p].r0); 91 swap(val[p].l1,val[p].r1); 92 } 93 void pushdown(int p) 94 { 95 if (rev[p]) 96 { 97 swap(c[p][0],c[p][1]); rev[p]^=1; 98 rever(c[p][0]),rever(c[p][1]); 99 } 100 if (ops[p]) 101 { 102 rollback(c[p][0]),rollback(c[p][1]); ops[p]^=1; 103 } 104 } 105 int find(int p,int x) 106 { 107 pushdown(p); 108 int l=c[p][0],r=c[p][1]; 109 if(sz[l]+1==x)return p; 110 else if(sz[l]>=x) return find(l,x);else return find(r,x-sz[l]-1); 111 } 112 int main() 113 { 114 n=read(),m=read(); 115 scanf("%s",ch+2); 116 for (int i=2;i<=n+1;i++) 117 if(ch[i]=='(')a[i]=1;else a[i]=-1; 118 build(rt,1,n+2,0); 119 while(m--) 120 { 121 int t=read(),l=read(),r=read(); 122 l=find(rt,l),r=find(rt,r+2); 123 splay(l,rt),splay(r,c[rt][1]); 124 int p=c[r][0]; 125 if(!t)printf("%d ",(val[p].r1+1)/2-(val[p].l0-1)/2);//左边取相反数. 126 else if (t==1) rollback(p); else rever(p); 127 } 128 }