题目描述
一个合法的括号序列是这样定义的:
-
空串是合法的。
-
如果字符串 S 是合法的,则(S)也是合法的。
- 如果字符串 A 和 B 是合法的,则 AB 也是合法的。
现在给你一个长度为 N 的由‘('和‘)'组成的字符串,位置标号从 1 到 N。对这个字符串有下列四种操作:
-
Replace a b c:将[a,b]之间的所有括号改成 c。例如:假设原来的字符串为:))())())(,那么执行操作 Replace 2 7 ( 后原来的字符串变为:)(((((()(。
-
Swap a b:将[a,b]之间的字符串翻转。例如:假设原来的字符串为:))())())(,那么执行操作 Swap 3 5 后原来的字符串变为:))))(())(。
-
Invert a b:将[a,b]之间的‘(’变成‘)’,‘)’变成‘(’。例如:假设原来的字符串为:))())())(,那么执行操作 Invert 4 8 后原来的字符串变为:))((()(((。
- Query a b:询问[a,b]之间的字符串至少要改变多少位才能变成合法的括号序列。改变某位是指将该位的‘(’变成‘)’或‘)’变成‘(’。注意执行操作 Query 并不改变当前的括号序列。例如:假设原来的字符串为:))())())(,那么执行操作 Query 3 6
的结果为 2,因为要将位置 5 的‘)’变成‘(’并将位置 6 的‘(’变成‘)’。
输入输出格式
输入格式:
从文件input.txt中读入数据,输入文件的第一行是用空格隔开的两个正整数N和M,分别表示字符串的长度和将执行的操作个数。第二行是长度为N的初始字符串S。接下来的M行是将依次执行的M个操作,其中操作名与操作数之间以及相邻操作数之间均用空格隔开。30%的数据满足
N,M≤3000。100%的数据满足N,M≤100000。
输出格式:
输出文件 output.txt 包含 T 行,其中 T 是输入的将执行的 M 个操作中 Query 操作出现的次数。Query 操作的每次出现依次对应输出文件中的一行,该行只有一个非负整数,表示执行对应 Query 操作的结果,即:所指字符串至少要改变多少位才能变成合法的括号序列。输入数据
保证问题有解。
输入输出样例
4 5 (((( Replace 1 2 ) Query 1 2 Swap 2 3 Invert 3 4 Query 1 4
1 2
说明
样例解释:输入中有2个Query操作,所以输出有2行。执行第一个Query操作时的括号序列为))((,因改变第1位可使[1,2]之间的字符串变成合法的括号序列,故输出的第一行为1。执行第二个Query操作时的括号序列为)((),因要改变第1位和第2位才能使[1,4]之间的字符串变成合法的括号序列,故输出的第二行为2。
题解:
终于找到一道不那么水的模板题了,这道题思维难度还是很高的,一开始看到有翻转操作就知道是splay,兴奋地开始码代码,然后看到取反操作,懵逼?!
这道题如果没有取反操作就万事大吉,那么我们现在假设万事大吉:
1.直接在splay树里面维护两个数a,b分别表示除去所有配对的括号后左边还有多少个右括号,右边还有多少个左括号。
2.瞎几把维护一下。
3.最后输出的就是$frac{a+1}{2}+frac{b+1}{2}$(实际上就是a和b分别除以2然后向上取整)。
最后输出的答案还是很好理解的,就当把a的一半反过来,把b的一半反过来。
但是...理想是美好的,现实是残酷的(因为并没有什么所谓的万事大吉)。
那么怎么进行取反操作呢?显然上述的方法是不行的,为什么呢?
因为上面的方法将已经配对的括号去掉了,然而进过取反操作,这些已经配对好了的括号就有可能会被拆开,从而形成新的未配对的括号。
也就是说上面的方法对于反转操作来说是有后效性的。那我们该怎么办呢?
我们将所有的“(”设为1,将所有的“)”设为-1,那么对于一对匹配的括号,他们的前缀和就是0。
很快我们就可以发现上文所说的a实际上就是最小的前缀和,上文所说的b实际上就是最大的后缀和,那么最大前缀和和最小后缀和在这道题中有没有用呢?
在这里我们令:
1.lmax表示最大前缀和
2.lmin表示最小前缀和
3.rmax表示最大后缀和
4.rmin表示最小后缀和
我们发现一个括号序列取反了之后,lmax=-lmin,lmin=-lmax,rmin=-rmax,rmax=-rmin。(不懂的话可以自己列一个序列试试)
那么我们在splay中维护:lmax,lmin,rmax,rmin。(至于怎么维护,相信大家都是知道的)
最后输出的就是$frac{lmin+1}{2}+frac{rmax+1}{2}$ 当然都要取绝对值。
然后这道题还有一些细节:
1.同时置为某数的操作下,我们要把取反的标记清0。而取反的标记下,我们要把置为某数的标记取反。
2.标记下传的时候,先传取反,再传置数。
为什么这样是对的呢?
1.假设是先取反再置为某数:我们经历了置数的修改,那么就把取反的标记清0了,这时我们只有置为某数的标记,所以答案是对的
2.假设是先置为某数再取反:我们经历了取反的修改,此时置的数也取反了。我们先执行取反操作,没错吧?然后再执行置数操作,这时由于置数取反了一遍,所以得到的也是对的
1 //Never forget why you start 2 #include<iostream> 3 #include<cstdio> 4 #include<cstdlib> 5 #include<cstring> 6 #include<cmath> 7 #include<algorithm> 8 #define ll(x) bst[x].child[0] 9 #define rr(x) bst[x].child[1] 10 #define son(x,t) bst[x].child[t] 11 using namespace std; 12 int n,m,a[100005],root,cnt; 13 char s[100005]; 14 struct BST{ 15 int child[2],fa,x,size; 16 int lmax,lmin,rmax,rmin,sum; 17 int lazy,rev,flag; 18 }bst[100005]; 19 void push_up(int root){ 20 bst[root].sum=bst[ll(root)].sum+bst[rr(root)].sum+bst[root].x; 21 bst[root].size=bst[ll(root)].size+bst[rr(root)].size+1; 22 bst[root].lmax=max(bst[ll(root)].lmax,bst[ll(root)].sum+bst[root].x+max(0,bst[rr(root)].lmax)); 23 bst[root].lmin=min(bst[ll(root)].lmin,bst[ll(root)].sum+bst[root].x+min(0,bst[rr(root)].lmin)); 24 bst[root].rmax=max(bst[rr(root)].rmax,bst[rr(root)].sum+bst[root].x+max(0,bst[ll(root)].rmax)); 25 bst[root].rmin=min(bst[rr(root)].rmin,bst[rr(root)].sum+bst[root].x+min(0,bst[ll(root)].rmin)); 26 } 27 void add_rev(int root){ 28 if(!root)return; 29 bst[root].rev^=1; 30 swap(ll(root),rr(root)); 31 swap(bst[root].lmax,bst[root].rmax); 32 swap(bst[root].lmin,bst[root].rmin); 33 } 34 void add_flag(int root){ 35 if(!root)return; 36 bst[root].flag^=1; 37 bst[root].x=-bst[root].x; 38 bst[root].sum=-bst[root].sum; 39 if(bst[root].lazy)bst[root].lazy=bst[root].lazy==1?-1:1; 40 swap(bst[root].lmax,bst[root].lmin); 41 bst[root].lmax=-bst[root].lmax; 42 bst[root].lmin=-bst[root].lmin; 43 swap(bst[root].rmax,bst[root].rmin); 44 bst[root].rmax=-bst[root].rmax; 45 bst[root].rmin=-bst[root].rmin; 46 } 47 void add_lazy(int root,int x){ 48 if(!root)return; 49 bst[root].flag=0; 50 bst[root].lazy=x; 51 bst[root].x=x; 52 bst[root].sum=bst[root].size*x; 53 if(x==1){ 54 bst[root].lmin=bst[root].rmin=0; 55 bst[root].lmax=bst[root].rmax=bst[root].size*x; 56 } 57 else{ 58 bst[root].lmin=bst[root].rmin=bst[root].size*x; 59 bst[root].lmax=bst[root].rmax=0; 60 } 61 } 62 void push_down(int root){ 63 if(bst[root].rev){ 64 add_rev(ll(root)); 65 add_rev(rr(root)); 66 bst[root].rev=0; 67 } 68 if(bst[root].flag){ 69 add_flag(ll(root)); 70 add_flag(rr(root)); 71 bst[root].flag=0; 72 } 73 if(bst[root].lazy){ 74 add_lazy(ll(root),bst[root].lazy); 75 add_lazy(rr(root),bst[root].lazy); 76 bst[root].lazy=0; 77 } 78 } 79 void build(int &root,int left,int right,int fa){ 80 int mid=(left+right)>>1; 81 root=++cnt; 82 bst[root].fa=fa; 83 bst[root].size=1; 84 bst[root].x=a[mid]; 85 bst[root].sum=a[mid]; 86 if(left==right){ 87 bst[root].lmin=min(bst[root].lmin,a[mid]); 88 bst[root].lmax=max(bst[root].lmax,a[mid]); 89 bst[root].rmin=min(bst[root].rmin,a[mid]); 90 bst[root].rmax=max(bst[root].rmax,a[mid]); 91 bst[root].sum=a[mid]; 92 } 93 if(left<mid)build(ll(root),left,mid-1,root); 94 if(mid<right)build(rr(root),mid+1,right,root); 95 push_up(root); 96 } 97 void rotate(int r,int t){ 98 int fa=bst[r].fa; 99 son(fa,!t)=son(r,t);bst[son(r,t)].fa=fa; 100 son(bst[fa].fa,son(bst[fa].fa,1)==fa)=r;bst[r].fa=bst[fa].fa; 101 son(r,t)=fa;bst[fa].fa=r; 102 push_up(fa); 103 push_up(r); 104 } 105 void splay(int r,int goal){ 106 int fa=bst[r].fa; 107 while(fa!=goal){ 108 if(bst[fa].fa==goal)rotate(r,son(fa,0)==r); 109 else{ 110 int t=son(bst[fa].fa,0)==fa; 111 if(son(fa,t)==r)rotate(r,!t),rotate(r,t); 112 else rotate(fa,t),rotate(r,t); 113 } 114 fa=bst[r].fa; 115 } 116 if(goal==0)root=r; 117 } 118 int find(int root,int k){ 119 push_down(root); 120 int y=bst[ll(root)].size; 121 if(y+1==k)return root; 122 else if(y>=k)return find(ll(root),k); 123 else return find(rr(root),k-y-1); 124 } 125 int split(int l,int r){ 126 l--,r++; 127 int x=find(root,l),y=find(root,r); 128 splay(x,0); 129 splay(y,x); 130 return ll(y); 131 } 132 int main(){ 133 int i,j,len; 134 scanf("%d%d",&n,&m); 135 scanf("%s",s+1); 136 len=strlen(s+1); 137 for(i=1;i<=len;i++){ 138 if(s[i]==')')a[i+1]=-1; 139 else a[i+1]=1; 140 } 141 len+=2; 142 build(root,1,len,0); 143 for(i=1;i<=m;i++){ 144 char ch[10],ss[10]; 145 int a,b; 146 scanf("%s%d%d",ch,&a,&b); 147 a++;b++; 148 if(ch[0]=='R'){ 149 char ch[3];scanf("%s",ch); 150 int x=split(a,b); 151 add_lazy(x,ch[0]=='('?1:-1); 152 push_up(bst[x].fa);push_up(root); 153 } 154 else if(ch[0]=='S'){ 155 int x=split(a,b); 156 add_rev(x); 157 push_up(bst[x].fa);push_up(root); 158 } 159 else if(ch[0]=='I'){ 160 int x=split(a,b); 161 add_flag(x); 162 push_up(bst[x].fa);push_up(root); 163 } 164 else if(ch[0]=='Q'){ 165 int x=split(a,b); 166 printf("%d ",(abs(bst[x].lmin)+1)/2+(abs(bst[x].rmax)+1)/2); 167 } 168 } 169 return 0; 170 }