• [HNOI2011]括号修复


    题目描述

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

    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
    ",(abs(bst[x].lmin)+1)/2+(abs(bst[x].rmax)+1)/2);
    167     }
    168   }
    169   return 0;
    170 }
  • 相关阅读:
    Unity Shader入门精要之 screen post-processing effect
    Unity---关于游戏小包的记录
    计算机基础补充之原码、反码、补码
    C#接口实现技巧之借助第三方
    lua中产生 1
    游戏文本本地化(一)
    xlua修复C#的委托事件的时候,需要提前做好配置
    xlua 实现协程替换Unity中的协程
    C#定义只能处理枚举类型的泛型类型
    C# : 泛型的继承关系实现的一个可以存放不同数据类型的链表
  • 原文地址:https://www.cnblogs.com/huangdalaofighting/p/8277660.html
Copyright © 2020-2023  润新知