码迷,mamicode.com
首页 > 其他好文 > 详细

[HNOI2011]括号修复

时间:2018-01-12 22:41:18      阅读:201      评论:0      收藏:0      [点我收藏+]

标签:输入输出格式   怎么办   格式   cst   build   ace   math   namespace   注意   

题目描述

一个合法的括号序列是这样定义的:

  1. 空串是合法的。

  2. 如果字符串 S 是合法的,则(S)也是合法的。

  3. 如果字符串 A 和 B 是合法的,则 AB 也是合法的。

现在给你一个长度为 N 的由‘(‘和‘)‘组成的字符串,位置标号从 1 到 N。对这个字符串有下列四种操作:

  1. Replace a b c:将[a,b]之间的所有括号改成 c。例如:假设原来的字符串为:))())())(,那么执行操作 Replace 2 7 ( 后原来的字符串变为:)(((((()(。

  2. Swap a b:将[a,b]之间的字符串翻转。例如:假设原来的字符串为:))())())(,那么执行操作 Swap 3 5 后原来的字符串变为:))))(())(。

  3. Invert a b:将[a,b]之间的‘(’变成‘)’,‘)’变成‘(’。例如:假设原来的字符串为:))())())(,那么执行操作 Invert 4 8 后原来的字符串变为:))((()(((。

  4. 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 操作的结果,即:所指字符串至少要改变多少位才能变成合法的括号序列。输入数据

保证问题有解。

输入输出样例

输入样例#1: 
4 5
((((
Replace 1 2 )
Query 1 2
Swap 2 3
Invert 3 4
Query 1 4
输出样例#1: 
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\n",(abs(bst[x].lmin)+1)/2+(abs(bst[x].rmax)+1)/2);
167     }
168   }
169   return 0;
170 }

 

 

 

[HNOI2011]括号修复

标签:输入输出格式   怎么办   格式   cst   build   ace   math   namespace   注意   

原文地址:https://www.cnblogs.com/huangdalaofighting/p/8277660.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!