hdu5217-括号序列线段树
Posted Konjak谷弱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hdu5217-括号序列线段树相关的知识,希望对你有一定的参考价值。
题意:给一串括号,有2个操作,1。翻转某个括号。2。查询某段区间内化简后第k个括号是在原序列中的位置。1 ≤ N,Q ≤ 200000.
题解:
可以知道,化简后的序列一定是)))((((这种形式的。
线段树每个节点就存对应区间内化简后的ls也就是)的数量,rs也就是(的数量。
然后我先把区间[l,r]找出来合并一遍,找出第k个是哪一种扩号。
问题转化为找区间[l,r]中的第kk个左扩号或者右括号。
我们可以发现,如果是)这种括号,区间从左到右合并的时候是单调不减的。
同理,(这种括号,区间从右往左合并的时候也是单调不减的。
然后我是变成从左往右的第kk个),或者从右往左的第kk个(。
[l,r]这个区间在线段树里可能由若干个区间组合而来,我们就根据左右括号的不同从左或从右合一遍,恰好遇到第kk个的时候就进去找。这个找就简单很多,因为它就是在线段树上走一遍的。
细节挺多的。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<iostream>
5 #include<algorithm>
6 using namespace std;
7
8 const int N=200010;
9 int n,m,tl,cl,c[N];
10 char s[N];
11 struct node{
12 int l,r,lc,rc,ls,rs;// ls ))) rs (((
13 }t[2*N];
14
15 int maxx(int x,int y){return x>y ? x:y;}
16
17 node upd(node x,node lc,node rc)
18 {
19 x.ls=lc.ls;
20 x.rs=rc.rs;
21 int sum=rc.ls-lc.rs;
22 if(sum>0) x.ls+=sum;
23 else x.rs+=-sum;
24 // x.ls=maxx(0,lc.ls+rc.ls-lc.rs);
25 // x.rs=maxx(0,rc.rs+lc.rs-rc.ls);
26 return x;
27 }
28
29 int bt(int l,int r)
30 {
31 int x=++tl;
32 t[x].l=l;t[x].r=r;
33 t[x].lc=t[x].rc=0;
34 t[x].ls=t[x].rs=0;
35 if(l<r)
36 {
37 int mid=(l+r)/2;
38 t[x].lc=bt(l,mid);
39 t[x].rc=bt(mid+1,r);
40 int lc=t[x].lc,rc=t[x].rc;
41 t[x]=upd(t[x],t[lc],t[rc]);
42 }
43 else
44 {
45 if(s[l]==‘)‘) t[x].ls=1;
46 else t[x].rs=1;
47 }
48 return x;
49 }
50
51 void change(int x,int p)
52 {
53 if(t[x].l==t[x].r) {swap(t[x].ls,t[x].rs);return ;}
54 int lc=t[x].lc,rc=t[x].rc,mid=(t[x].l+t[x].r)/2;
55 if(p<=mid) change(lc,p);
56 else change(rc,p);
57 t[x]=upd(t[x],t[lc],t[rc]);
58 }
59
60 void query(int x,int l,int r)
61 {
62 if(t[x].l==l && t[x].r==r) {c[++cl]=x;return;}
63 int lc=t[x].lc,rc=t[x].rc,mid=(t[x].l+t[x].r)/2;
64 if(r<=mid) query(lc,l,r);
65 else if(l>mid) query(rc,l,r);
66 else
67 {
68 query(lc,l,mid);
69 query(rc,mid+1,r);
70 }
71 }
72
73 int fd(int x,int k,int tmp)
74 {
75 if(t[x].l==t[x].r) return t[x].l;
76 int lc=t[x].lc,rc=t[x].rc;
77 if(tmp==0)
78 {
79 if(t[lc].ls>=k) return fd(lc,k,tmp);
80 return fd(rc,k-t[lc].ls+t[lc].rs,tmp);
81 }
82 else
83 {
84 if(t[rc].rs>=k) return fd(rc,k,tmp);
85 return fd(lc,k-t[rc].rs+t[rc].ls,tmp);
86 }
87 }
88
89 int main()
90 {
91 freopen("a.in","r",stdin);
92 freopen("a.out","w",stdout);
93 int T;
94 scanf("%d",&T);
95 while(T--)
96 {
97 scanf("%d%d",&n,&m);
98 scanf("%s",s+1);
99 tl=0;bt(1,n);
100 for(int i=1;i<=m;i++)
101 {
102 int tmp,x,l,r,k,ans;
103 scanf("%d",&tmp);
104 if(tmp==1)
105 {
106 scanf("%d",&x);
107 change(1,x);
108 }
109 else
110 {
111 scanf("%d%d%d",&l,&r,&k);
112 cl=0;query(1,l,r);
113 node now=t[c[1]];
114 for(int j=2;j<=cl;j++) now=upd(now,now,t[c[j]]);
115 if(now.ls+now.rs<k) {printf("-1\n");continue;}
116 if(now.ls>=k)
117 {
118 node p0=t[c[1]],p1;
119 if(p0.ls>=k) ans=fd(c[1],k,0);
120 else
121 {
122 for(int j=2;j<=cl;j++)
123 {
124 p1=upd(p1,p0,t[c[j]]);
125 if(p1.ls>=k)
126 {
127 ans=fd(c[j],k-p0.ls+p0.rs,0);
128 break;
129 }
130 p0=p1;
131 }
132 }
133 }
134 else
135 {
136 k=now.ls+now.rs-k+1;
137 node p0=t[c[cl]],p1;
138 if(p0.rs>=k) ans=fd(c[cl],k,1);
139 else
140 {
141 for(int j=cl-1;j>=1;j--)
142 {
143 p1=upd(p1,t[c[j]],p0);
144 if(p1.rs>=k) {ans=fd(c[j],k-p0.rs+p0.ls,1);break;}
145 p0=p1;
146 }
147 }
148 }
149 printf("%d\n",ans);
150 }
151 }
152 }
153 return 0;
154 }
以上是关于hdu5217-括号序列线段树的主要内容,如果未能解决你的问题,请参考以下文章
HDU 4521 小明系列问题——小明序列 (线段树维护DP)
gym102889J线段树维护最大最小前缀和判断合法括号序列