【bzoj2329】[HNOI2011]括号修复 splay

先考虑一下,给定一个括号序列,怎样求答案。

括号序列化简后一定是这个样子的,)))))(((

这样子的答案就是(左括号数+1)/2+(右括号数+1)/2

那么,我们只需要维护好从左边有多少个右括号和从右边有多少个左括号就可以求答案了。

记(为-1,)为1,这样()相加后就被抵消了。

于是,我们只需要维护一个类似最大连续子段和中的从左边延伸多少就可以了。

操作其实都非常基础。

覆盖标记时,记得把其他标记全部清零。

反转标记时,记得吧覆盖标记反转。


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#define maxn 100010

using namespace std;

int ch[maxn][2],fa[maxn],lmax[maxn],rmax[maxn],lmin[maxn],rmin[maxn],w[maxn],sum[maxn],size[maxn];
bool tag[maxn],rev[maxn];
int flag[maxn],a[maxn];
char s[maxn];
int n,m,tot,T,root;

int dir(int x)
{
	return x==ch[fa[x]][1];
}

void update(int x)
{
	size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
	sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+w[x];
	lmax[x]=max(lmax[ch[x][0]],sum[ch[x][0]]+w[x]+lmax[ch[x][1]]);
	rmax[x]=max(rmax[ch[x][1]],sum[ch[x][1]]+w[x]+rmax[ch[x][0]]);
	lmin[x]=min(lmin[ch[x][0]],sum[ch[x][0]]+w[x]+lmin[ch[x][1]]);
	rmin[x]=min(rmin[ch[x][1]],sum[ch[x][1]]+w[x]+rmin[ch[x][0]]);
}

void invert(int x)
{
	lmin[x]=-lmin[x];lmax[x]=-lmax[x];
	rmin[x]=-rmin[x];rmax[x]=-rmax[x];
	swap(lmax[x],lmin[x]);swap(rmax[x],rmin[x]);
	w[x]=-w[x];sum[x]=-sum[x];
	if (flag[x]) flag[x]=-flag[x]; else tag[x]^=1;
}

void reverse(int x)
{
	swap(lmax[x],rmax[x]);swap(lmin[x],rmin[x]);
	swap(ch[x][0],ch[x][1]);
	rev[x]^=1;
}

void replace(int x,int f)
{
	w[x]=f;sum[x]=size[x]*f;
	if (f==1) lmax[x]=rmax[x]=sum[x],lmin[x]=rmin[x]=0;
	if (f==-1) lmin[x]=rmin[x]=sum[x],lmax[x]=rmax[x]=0;
	tag[x]=0;rev[x]=0;flag[x]=f;
}

void push_down(int x)
{
	if (tag[x])
	{
		if (ch[x][0]) invert(ch[x][0]);
		if (ch[x][1]) invert(ch[x][1]);
		tag[x]=0;
	}
	if (rev[x])
	{
		if (ch[x][0]) reverse(ch[x][0]);
		if (ch[x][1]) reverse(ch[x][1]);
		rev[x]=0;
	}
	if (flag[x])
	{
		if (ch[x][0]) replace(ch[x][0],flag[x]);
		if (ch[x][1]) replace(ch[x][1],flag[x]);
		flag[x]=0;
	}
}

void down(int x)
{
	if (fa[x]) down(fa[x]);
	push_down(x);
}

void rotate(int x)
{
	int y,z,a,b,c;
	y=fa[x];z=fa[y];b=dir(x);a=ch[x][!b];
	if (z==0) root=x;
	else
	{
		c=dir(y);ch[z][c]=x;
	}
	fa[x]=z;fa[y]=x;ch[x][!b]=y;ch[y][b]=a;
	if (a) fa[a]=y;
	update(y);update(x);
}

void splay(int x,int i)
{
	down(x);
	int y,z,b,c;
	while (fa[x]!=i)
	{
		y=fa[x];z=fa[y];
		if (z==i) rotate(x);
		else
		{
			b=dir(x);c=dir(y);
			if (b^c)
			{
				rotate(x);rotate(x);
			}
			else
			{
				rotate(y);rotate(x);
			}
		}
	}
}

int find_k(int x,int k)
{
	push_down(x);
	if (size[ch[x][0]]==k-1) return x;
	if (size[ch[x][0]]>k-1) return find_k(ch[x][0],k);
	else return find_k(ch[x][1],k-size[ch[x][0]]-1);
}

void build_tree(int l,int r,int tt)
{
	int mid=(l+r)/2;
	w[tt]=a[mid];
	if (l==r) {size[tt]=1;sum[tt]=w[tt];if (w[tt]>0) lmax[tt]=rmax[tt]=1; else lmin[tt]=rmin[tt]=-1;return;}
	if (l<mid) {tot++;ch[tt][0]=tot;fa[tot]=tt;build_tree(l,mid-1,tot);}
	if (mid<r) {tot++;ch[tt][1]=tot;fa[tot]=tt;build_tree(mid+1,r,tot);}
	update(tt);
}

int query(int l,int r)
{
	int x=find_k(root,l);splay(x,0);
	int y=find_k(ch[x][1],r-l+2);splay(y,x);
	return (lmax[ch[y][0]]+1)/2+(-rmin[ch[y][0]]+1)/2;
}

void Reverse(int l,int r)
{
	int x=find_k(root,l);splay(x,0);
	int y=find_k(ch[x][1],r-l+2);splay(y,x);
	reverse(ch[y][0]);
}

void Replace(int l,int r,int d)
{
	int x=find_k(root,l);splay(x,0);
	int y=find_k(ch[x][1],r-l+2);splay(y,x);
	replace(ch[y][0],d);
}

void Invert(int l,int r)
{
	int x=find_k(root,l);splay(x,0);
	int y=find_k(ch[x][1],r-l+2);splay(y,x);
	invert(ch[y][0]);
}

int main()
{
	scanf("%d%d",&n,&T);
	scanf("%s",s+1);
	for (int i=1;i<=n;i++) if (s[i]=='(') a[i]=-1; else a[i]=1;
	tot=2;root=1;
	fa[1]=0;size[1]=2;ch[1][1]=2;
	fa[2]=1;size[2]=1;
	tot++;ch[2][0]=tot;fa[tot]=2;
	build_tree(1,n,tot);
	update(2);update(1);
	while (T--)
	{
		int x,y;
		scanf("%s%d%d",s,&x,&y);
		if (s[0]=='Q') printf("%d\n",query(x,y));
		if (s[0]=='S') Reverse(x,y);
		if (s[0]=='R')
		{
			int w;
			scanf("%s",s);
			if (s[0]=='(') w=-1; else w=1;
			Replace(x,y,w);
		}
		if (s[0]=='I') Invert(x,y);
	}
	return 0;
}


你可能感兴趣的:(【bzoj2329】[HNOI2011]括号修复 splay)