您的位置:首页 > Web前端 > JavaScript

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

2

2

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);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: