zoukankan      html  css  js  c++  java
  • bzoj2209 括号序列

    题意:给你一个括号序列。操作1:询问需要更改多少个括号使之匹配。

    操作2:反转序列,左括号变成右括号。

    操作3:翻转序列,倒置。

    标程:

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #define ls son[k][0]
     5 #define rs son[k][1]
     6 using namespace std;
     7 const int N=100005;
     8 int sum[N],mn_l[N],mx_l[N],mx_r[N],mn_r[N],a[N],son[N][2],rev[N],tag[N],n,t,l,r,sz[N],y,z,fa[N],Q,rt;
     9 char s[N];
    10 void up(int k)
    11 {
    12     sum[k]=sum[ls]+sum[rs]+a[k];sz[k]=sz[ls]+sz[rs]+1;
    13     mx_l[k]=max(mx_l[ls],sum[ls]+a[k]+mx_l[rs]);
    14     mn_l[k]=min(mn_l[ls],sum[ls]+a[k]+mn_l[rs]);
    15     mx_r[k]=max(mx_r[rs],sum[rs]+a[k]+mx_r[ls]);
    16     mn_r[k]=min(mn_r[rs],sum[rs]+a[k]+mn_r[ls]);
    17 }
    18 void work_rev(int k)//由于需要访问实际pushdown到点的下一层(son[r+1][0]),所以要对下一层的权值进行修改 
    19 {rev[k]^=1;swap(mx_l[k],mx_r[k]);swap(mn_l[k],mn_r[k]);}
    20 void work_opp(int k)
    21 {
    22     tag[k]^=1;
    23     sum[k]=-sum[k];a[k]=-a[k];
    24     mx_l[k]=-mx_l[k];mx_r[k]=-mx_r[k];mn_l[k]=-mn_l[k];mn_r[k]=-mn_r[k];
    25     swap(mx_l[k],mn_l[k]);swap(mx_r[k],mn_r[k]);
    26 }
    27 void down(int k)
    28 {
    29     if (rev[k])
    30     {
    31         swap(son[k][0],son[k][1]);work_rev(ls);work_rev(rs); 
    32         rev[k]=0;
    33     }
    34     if (tag[k])
    35     {
    36         work_opp(ls);work_opp(rs); 
    37         tag[k]=0;
    38     }
    39 }
    40 void rot(int &k,int x)
    41 {
    42     int y=fa[x],z=fa[y],l=(son[y][1]==x),r=l^1;//函数里面定义l,r,不要和询问的l,r混淆 
    43     if (y==k) k=x;else son[z][(son[z][1]==y)]=x;
    44     fa[y]=x;fa[x]=z;fa[son[x][r]]=y;
    45     son[y][l]=son[x][r];son[x][r]=y;
    46     up(y);up(x);
    47 }
    48 void spl(int &k,int x)
    49 {
    50     for (;x!=k;rot(k,x))
    51       if ((y=fa[x])!=k)
    52          if (son[y][0]==x^son[fa[y]][0]==y) rot(k,x);else rot(k,y);
    53 }
    54 void build(int l,int r,int f)
    55 {
    56     if (l>r) return; 
    57     int mid=(l+r)>>1;fa[mid]=f;
    58     if (f) son[f][(mid>f)]=mid;
    59     if (s[mid]=='(') a[mid]=1;else if (s[mid]==')') a[mid]=-1;
    60     if (l==r) 
    61     {
    62         sz[l]=1;sum[l]=a[l];
    63        if (a[l]>0) mx_l[l]=mx_r[l]=1;
    64        if (a[l]<0) mn_l[l]=mn_r[l]=-1;
    65        return;
    66     }
    67     build(l,mid-1,mid);build(mid+1,r,mid);
    68     up(mid);
    69 }
    70 int find(int k,int x)//因为有翻转操作,所以区间第x个与原下标为x的不对应 
    71 {
    72     down(k);
    73     if (x==sz[ls]+1) return k;
    74     if (x<=sz[ls]) return find(ls,x);else return find(rs,x-sz[ls]-1); 
    75 }
    76 int main()
    77 {
    78     scanf("%d%d%s",&n,&Q,s+2);s[1]='*';s[n+2]='*';
    79     build(1,n+2,0);rt=(n+3)/2;
    80     while (Q--)
    81     {
    82         scanf("%d%d%d",&t,&l,&r);
    83         l=find(rt,l);r=find(rt,r+2);
    84         spl(rt,l);spl(son[rt][1],r); int now=son[r][0];
    85         if (t==0) printf("%d
    ",(abs(mn_l[now])+1)/2+(abs(mx_r[now])+1)/2);
    86         else if (t==1) work_opp(now);
    87         else work_rev(now);
    88     }
    89    return 0;
    90 }

    易错点:1.由于需要访问实际pushdown到点的下一层(son[r+1][0]),所以要对下一层的权值进行修改 。pushdown的写法应根据实际情况来。

    2.函数里面定义l,r,不要和询问的l,r混淆。

    3.因为有翻转操作,所以区间第x个与原下标为x的不对应。所以要find到区间第x个数(二叉排序树维护区间位置)。

    题解:splay

    你看有翻转为什么不用splay呢?

    对于一串括号序列,将中间的匹配掉后剩下x个右括号和y个左括号。(x+y)是偶数时才可能完全匹配。此时如果x,y全偶,那么修改x/2+y/2个即可,全奇,修改(x+1)/2+(y+1)/2个即可。问题在于怎么求x和y。

    有性质,x=由左端点固定,右端点在[l,r]中的括号子序列中(右括号-左括号)的最大值,y=由右端点固定,左端点在[l,r]中的括号子序列中(左括号-右括号)的最大值。

    用+1-1表示左右括号,x=左边最大,y=右边最小。

    反转,就需要维护再维护左边最小和右边最大。用打标记的方法,标记下传时对四个关键值取负,并把左边/右边的最小最大交换。

    翻转维护rev标记即可。

  • 相关阅读:
    imperva_waf导入ssl证书
    博科光纤交换机初始化配置
    xss测试代码
    生成树注意事项
    [转载]Basics of the Unix Philosophy
    [转载]GSview注册码
    [转载]tar命令详解
    [转载]WinEdt 6 注册 试用期30天永不过期
    [转载+修改]计数排序
    [转载]C++ 关于声明,定义,类的定义,头文件作用,防止头文件在同一个编译单元重复引用,不具名空间
  • 原文地址:https://www.cnblogs.com/Scx117/p/8715183.html
Copyright © 2011-2022 走看看