Scx117
只一眼,便辽阔了时间。

题意:给你一个括号序列。操作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\n",(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标记即可。

posted on 2018-04-04 09:33  Scx117  阅读(131)  评论(0编辑  收藏  举报