bzoj2209 [Jsoi2011]括号序列(splay)
2017-11-26 20:28
429 查看
Description
Input
输入数据的第一行包含两个整数N和Q,分别表示括号序列的长度,以及操作的个数。 第二行包含一个长度为N的括号序列。 接下来Q行,每行三个整数t、x和y,分别表示操作的类型、操作的开始位置和操作的结 束位置,输入数据保证x不小于y。其中t=0表示询问操作、t=1表示反转操作、t=2表示翻转操 作。Output
对于每一个询问操作,输出一行,表示将括号序列的该子序列修改为配对,所需的最少改动 个数。Sample Input
6 3)(())(
0 1 6
0 1 4
0 3 4
Sample Output
22
0
HINT
100%的数据满足N,Q不超过10^5。分析:
之前做过一道匹配括号的题,标算是dp
但是那道题有两种括号,
这道题在括号种类上简化了一下
但是多出了两个操作
一开始我有一个简单的想法:
所有的“(”赋值成-1,“)”赋值成1
只需要判断区间的和是不是0即可
但是这样是连样例都过不了的
匹配询问:
实际上转换成+-1的思想是正确的:我们将括号序列改成-1与1的序列,用-1表示“(”,用1表示“)”
那么一段区间中前缀和的最大值就是未匹配的“)”的数量(mx1表示),
后缀和的最小值为未匹配的“(”的数量(mn2表示)。
如果未匹配的“(”,“)”都是偶数的话,那答案就是(mx1+mn2)/2
如果未匹配的“(”,“)”都是奇数的话,那答案就是(mx1+mn2)/2+1
反转操作:
把所有权值都*-1(mx1,mx2,mn1,mn2,sum,v)打反转标记,swap(mx1,mn1),swap(mx2,mn2)
因为反转之后-1与1都会发生变化,而且取反后最大值与最小值都会发生大小交换
翻转操作:
splay的经典操作(文艺平衡树),交换左右子树,打翻转标记,swap(mx1[bh],mx2[bh]),swap(mn1[bh],mn2[bh]),因为以前的前缀变成了后缀,数值并未发生改变
有两个标记会不会发生标记冲突呢?
这道题比较好,通过手动操作可以发现两个标记互不影响,先下放哪一个的效果都是相同的
对于问题的解决方法就到这里,下面就是细节的实现
我们先具象的了解一下mx和mn,并且弄明白翻转操作和反转操作的原理:
整道题难度都集中在了update函数上:
void update(int bh) { if (!bh) return; mx1[0]=mx2[0]=-INF; mn1[0]=mn2[0]=INF; v[0]=0; //防止儿子不存在(=0)的时候出错 int lc=ch[bh][0]; int rc=ch[bh][1]; size[bh]=1+size[lc]+size[rc]; sum[bh]=v[bh]+sum[lc]+sum[rc]; int t; t=max(sum[lc]+v[bh],sum[lc]+v[bh]+mx1[rc]); mx1[bh]=max(mx1[lc],t); t=min(sum[lc]+v[bh],sum[lc]+v[bh]+mn1[rc]); mn1[bh]=min(mn1[lc],t); t=max(sum[rc]+v[bh],sum[rc]+v[bh]+mx2[lc]); mx2[bh]=max(mx2[rc],t); t=min(sum[rc]+v[bh],sum[rc]+v[bh]+mn2[lc]); mn2[bh]=min(mn2[rc],t); }
实际上具体思路就和线段树的差不多
以mx1为例:
mx1[fa]的取值有三种可能:mx1[lc] , sum[lc]+v[fa] , sum[lc]+v[fa]+mx1[rc]
tip
注意update的写法//这里写代码片
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF=0x33333333;
const int N=500010;
int v
,sum
,ch
[2],pre
,size
;
int mx1
,mx2
,mn1
,mn2
; //mx1:最大前缀和,mx2:最大后缀和,mn1:最小前缀和,mn2:最小后缀和
bool rev
,mul
;
int n,top=0,m,a
,root;
char s
;
int get(int bh)
{
return ch[pre[bh]][0]==bh? 0:1;
}
void update(int bh) { if (!bh) return; mx1[0]=mx2[0]=-INF; mn1[0]=mn2[0]=INF; v[0]=0; //防止儿子不存在(=0)的时候出错 int lc=ch[bh][0]; int rc=ch[bh][1]; size[bh]=1+size[lc]+size[rc]; sum[bh]=v[bh]+sum[lc]+sum[rc]; int t; t=max(sum[lc]+v[bh],sum[lc]+v[bh]+mx1[rc]); mx1[bh]=max(mx1[lc],t); t=min(sum[lc]+v[bh],sum[lc]+v[bh]+mn1[rc]); mn1[bh]=min(mn1[lc],t); t=max(sum[rc]+v[bh],sum[rc]+v[bh]+mx2[lc]); mx2[bh]=max(mx2[rc],t); t=min(sum[rc]+v[bh],sum[rc]+v[bh]+mn2[lc]); mn2[bh]=min(mn2[rc],t); }
void change(int bh) //处理翻转操作
{
swap(mx1[bh],mx2[bh]);
swap(mn1[bh],mn2[bh]);
}
void change1(int bh) //处理反转操作(括号取反)
{
sum[bh]*=-1; v[bh]*=-1;
mx1[bh]*=-1; mx2[bh]*=-1;
mn1[bh]*=-1; mn2[bh]*=-1;
swap(mx1[bh],mn1[bh]);
swap(mx2[bh],mn2[bh]);
}
void push(int bh)
{
if (!bh) return;
if (rev[bh])
{
if (ch[bh][0]) change(ch[bh][0]),rev[ch[bh][0]]^=1;
if (ch[bh][1]) change(ch[bh][1]),rev[ch[bh][1]]^=1;
swap(ch[bh][0],ch[bh][1]);
rev[bh]^=1;
}
if (mul[bh])
{
if (ch[bh][0]) change1(ch[bh][0]),mul[bh[ch][0]]^=1;
if (ch[bh][1]) change1(ch[bh][1]),mul[bh[ch][1]]^=1;
mul[bh]^=1;
}
}
void rotate(int bh)
{
int fa=pre[bh];
int grand=pre[fa];
int wh=get(bh);
ch[fa][wh]=ch[bh][wh^1];
pre[ch[fa][wh]]=fa;
ch[bh][wh^1]=fa;
pre[fa]=bh;
pre[bh]=grand;
if (grand) ch[grand][ch[grand][0]==fa? 0:1]=bh;
update(fa);
update(bh);
}
void down(int bh)
{
if (pre[bh]) down(pre[bh]);
push(bh);
}
void splay(int bh,int mb)
{
down(bh);
for (int fa;(fa=pre[bh])!=mb;rotate(bh))
if (pre[fa]!=mb)
rotate(get(bh)==get(fa)? fa:bh);
if (mb==0) root=bh;
}
int find(int x)
{
int now=root;
while (1)
{
push(now);
if (size[ch[now][0]]>=x) now=ch[now][0];
else{
int tmp=(ch[now][0]? size[ch[now][0]]:0);
tmp++;
if (x<=tmp) return now;
x-=tmp;
now=ch[now][1];
}
}
}
int build(int l,int r,int fa)
{
if (l>r) return 0;
int now=++top;
int mid=(l+r)>>1;
ch[now][0]=build(l,mid-1,now);
ch[now][1]=build(mid+1,r,now);
pre[now]=fa;
v[now]=a[mid]; ///
update(now);
return now;
}
int main()
{
scanf("%d%d",&n,&m);
scanf("%s",s+1);
for (int i=1;i<=n;i++)
if (s[i]=='(') a[i]=-1;
else a[i]=1;
root=build(0,n+1,0);
for (int i=1;i<=m;i++)
{
int opt,x,y;
scanf("%d%d%d",&opt,&x,&y);
int xx=find(x);
int yy=find(y+2);
splay(xx,0);
splay(yy,xx);
int now=ch[ch[root][1]][0];
if (opt==0){
int ans1=mx1[now]; int ans2=mn2[now];
//前缀和的最大值就是未匹配的")"的数量,后缀和的最小值为未匹配的"("的数量
if (ans2>0) ans2=0;
else ans2=-ans2;
if (ans1<0) ans1=0;
if ((ans1&1)||(ans2&1)) printf("%d\n",(ans1+ans2)/2+1);
else printf("%d\n",(ans1+ans2)/2);
}
else if (opt==1){
mul[now]^=1;
change1(now);
update(ch[root][1]); update(root);
}
else{
rev[now]^=1;
change(now);
update(ch[root][1]); update(root);
}
}
}
相关文章推荐
- bzoj 2209: [Jsoi2011]括号序列 splay
- 【BZOJ】2209: [Jsoi2011]括号序列(splay)
- BZOJ 2209: [Jsoi2011]括号序列 [splay 括号]
- 【BZOJ2329/2209】[HNOI2011]括号修复/[Jsoi2011]括号序列 Splay
- [BZOJ2209][[Jsoi2011]括号序列][splay]
- [BZOJ2209][JSOI2011]括号序列(splay)
- bzoj 2209: [Jsoi2011]括号序列(splay)
- 【BZOJ2209】括号序列(JSOI2011)-Splay
- BZOJ 2209 [Jsoi2011] 括号序列
- ●BZOJ 2209 [Jsoi2011]括号序列
- bzoj2209: [Jsoi2011]括号序列
- BZOJ2209 [Jsoi2011]括号序列
- bzoj2209[Jsoi2011] 括号序列
- BZOJ 2209: [Jsoi2011]括号序列&&BZOJ 2329: [HNOI2011]括号修复
- BZOJ 2209 括号序列(splay)
- BZOJ P2209:[Jsoi2011]括号序列
- bzoj2209括号序列splay
- 【BZOJ-2329&2209】括号修复&括号序列 Splay
- bzoj 2209 括号序列
- BZOJ 2329 HNOI2011 括号修复 Splay