【BZOJ 1500】 [NOI2005]维修数列

1500: [NOI2005]维修数列

Time Limit: 10 Sec   Memory Limit: 64 MB
Submit: 6672   Solved: 2016
[ Submit][ Status]

Description

Input

输入文件的第1行包含两个数N和M,N表示初始时数列中数的个数,M表示要进行的操作数目。第2行包含N个数字,描述初始时的数列。以下M行,每行一条命令,格式参见问题描述中的表格。

Output

对于输入数据中的GET-SUM和MAX-SUM操作,向输出文件依次打印结果,每个答案(数字)占一行。

Sample Input

9 8
2 -6 3 5 1 -5 -3 6 3
GET-SUM 5 4
MAX-SUM
INSERT 8 3 -5 7 2
DELETE 12 1
MAKE-SAME 3 3 2
REVERSE 3 6
GET-SUM 5 4
MAX-SUM

Sample Output

-1
10
1
10

非常麻烦的splay模板题!!写完之后感觉对splay操作以及Push_up() Push_down()操作有了更深的理解!


Insert(),Delete(),Reverse()操作和【BZOJ 1269】完全一致。

Make-same():在每个节点上维护一个modi的信息,如果是true说明整棵子树都被修改为a[x].data,否则没有改动

Get-sum():每个结点维护一个sum,a[x].sum=a[a[x].l].sum+a[a[x].r].sum+a[x].data

Maxsum():这个询问最为复杂,需要维护三个值: maxl;maxr;maxm。(如何维护在Push_up操作中说)


小结一下:每个结点需要维护11个值

(1)l:左儿子

(2)r:右儿子

(3)fa:父亲

(4)data:此结点所代表的数

(5)size:区间长度

(6)rev:是否被反转

(7)modi:是否被修改为其他的数

(8)sum:此区间的所有数之和

(9)maxl:从区间最左端向右延伸的最大和

(10)maxr:从区间最右端向左延伸的最大和

(11)maxm:整个区间的最大子序列和


那么本题最关键的部分就在于Push_down和Push_up操作了!!


Push_up(x):

Push_up操作就是当儿子结点有修改之后要更新父亲结点。


size和sum很好维护,说一下maxl,maxr,maxm的维护方法:


(一)maxl:分两种情况:

(1)不包含x:a[a[x].l].maxl;

(2)包含x:而包含x中如果右子树maxl>0就加上否则不加a[a[x].l].sum+a[x].data+max(0,a[a[x].r].maxl))


(二)maxr同maxl。


(三)maxm:分两种情况

(1)不包含x:max(a[a[x].l].maxm,a[a[x].r].maxm)

(2)包含x:如果左子树的maxr>0就加上,右子树的maxl>0也加上a[x].data+max(0,a[a[x].l].maxr)+max(0,a[a[x].r].maxl)



Push_down(x):

Push_down操作是指对整棵子树的根结点进行标记后,整棵子树的每一个结点都要修改。Push_down(x)后x结点的标记下传,则x结点的信息的已经维护好了,以后维护x的儿子。


分两种情况:

(1)如果a[x].rev即需要反转:把rev标记下传,交换左右儿子,交换maxl,maxr

(2)如果a[x].modi即需要修改权值:修改子树权值并把标记下传;对于当前结点的maxl,maxr,maxm,如果修改后的权值>0就抖赋值为权值*size,否则赋值为一个权值。



下面贴代码:

(标注的1、2、3后文解释)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#define maxx 0x3f3f3f3f
#define maxn 500010
using namespace std;
struct splay
{
	int l,r,fa,size,sum,data,modi,maxl,maxr,maxm,rev;
}a[maxn];
char str[maxn];
int tot,f[maxn],root=0,n,m,pos,tot1=0,tot2=0,s[maxn];
void Push_up(int x)
{
	if (!x) return;
	int l=a[x].l,r=a[x].r;
	a[x].size=1+a[a[x].l].size+a[a[x].r].size;
	a[x].sum=a[a[x].l].sum+a[a[x].r].sum+a[x].data;
	//a[x].maxl
	int ma;
	int pr=0,pl=0;
	if (a[r].maxl>0) pr=a[r].maxl;
	if (a[l].maxl>a[l].sum+a[x].data+pr) ma=a[l].maxl;
	else ma=a[l].sum+a[x].data+pr;
	a[x].maxl=ma;
        //a[x].maxr
	if (a[l].maxr>0) pl=a[l].maxr;
	if (a[r].maxr>a[r].sum+a[x].data+pl) ma=a[r].maxr;
	else ma=a[r].sum+a[x].data+pl;
	a[x].maxr=ma;
	//a[x].maxm
	pl=pr=0;
	if (a[l].maxm>a[r].maxm) ma=a[l].maxm;
	else ma=a[r].maxm;
	if (a[r].maxl>0) pr=a[r].maxl;
	if (a[l].maxr>0) pl=a[l].maxr;
	if (ma<pl+a[x].data+pr) ma=pl+a[x].data+pr;
	a[x].maxm=ma;
}
void Push_down(int x)
{
        if (!x) return;
	if (a[x].rev)
	{
		a[x].rev=0;
		swap(a[x].maxl,a[x].maxr);
		swap(a[x].l,a[x].r);
		a[a[x].l].rev^=1;
		a[a[x].r].rev^=1;
	}
	if (a[x].modi)
	{
		a[a[x].l].data=a[a[x].r].data=a[x].data;
		if (a[x].data>=0) a[x].maxl=a[x].maxr=a[x].maxm=a[x].data*a[x].size;
		else a[x].maxl=a[x].maxr=a[x].maxm=a[x].data;
		a[x].sum=a[x].data*a[x].size;
		a[a[x].l].modi=a[a[x].r].modi=1;
		a[x].modi=0;
	}
}
void New_node(int &x,int fa,int key)
{
	if (tot2) x=s[tot2--];
	else x=++tot1;
	a[x].fa=fa;
	a[x].data=a[x].maxl=a[x].maxr=a[x].maxm=key;
	a[x].l=a[x].r=a[x].rev=a[x].size=0;
	a[x].modi=0;
}
void Build(int &x,int fa,int l,int r)
{
	if (l>r) return;
	int m=(l+r)>>1;
	New_node(x,fa,f[m]);
	Build(a[x].l,x,l,m-1);
	Build(a[x].r,x,m+1,r);
	Push_up(x);
}
int judge(char *str)
{
	if (str[0]=='I') return 1;
	if (str[0]=='D') return 2;
	if (str[0]=='M')
	{
		if (str[2]=='K') return 3;
		return 6;
        }
	if (str[0]=='R') return 4;
	return 5;
}
void zig(int x)
{
	int y=a[x].fa;
	int z=a[y].fa;
	Push_down(a[y].r);Push_down(a[x].l);Push_down(a[x].r);  //1
	a[x].fa=z,a[y].fa=x;
	a[y].l=a[x].r,a[a[x].r].fa=y,a[x].r=y;
	if (a[z].l==y) a[z].l=x;
	else a[z].r=x;
	Push_up(y);
}
void zag(int x)
{
	int y=a[x].fa;
	int z=a[y].fa;
	Push_down(a[y].l);Push_down(a[x].l);Push_down(a[x].r);
	a[x].fa=z,a[y].fa=x;
	a[y].r=a[x].l,a[a[x].l].fa=y,a[x].l=y;
	if (a[z].l==y) a[z].l=x;
	else a[z].r=x;
	Push_up(y);
}
void splay(int x,int s)
{
	Push_down(x);
	while (a[x].fa!=s)
	{
		int y=a[x].fa;
		int z=a[y].fa;
		if (z==s)
		{
			if (x==a[y].l) zig(x);
			else zag(x);
			break;
		}
		if (y==a[z].l)
		{
			if (x==a[y].l) zig(y),zig(x);
			else zag(x),zig(x);
		}
		else
		{
			if (x==a[y].r) zag(y),zag(x);
			else zig(x),zag(x);
		}
	}
	Push_up(x);
	if (s==0) root=x;
}
int Findkth(int x,int k)
{
	Push_down(x);
	int s=a[a[x].l].size;
	if (k==s+1) return x;
	if (k<=s) return Findkth(a[x].l,k);
	return Findkth(a[x].r,k-s-1);
}
int Getmin(int x)
{
	Push_down(x);
	while (a[x].l)
		x=a[x].l,Push_down(x);
	return x;
}
void Insert(int pos,int tot)
{
	int x=Findkth(root,pos);
	splay(x,0);
	x=Getmin(a[x].r);
	splay(x,root);
	Build(a[x].l,x,1,tot);
	splay(a[x].l,0);
}
void Out_memory(int x)   //2
{
	if (!x) return;
	s[++tot2]=x;
	Out_memory(a[x].l);
	Out_memory(a[x].r);
}
void Delet(int pos,int tot)
{
	int x=Findkth(root,pos);
	splay(x,0);
	x=Findkth(root,pos+tot+1);
	splay(x,root);
	Out_memory(a[x].l);
	a[x].l=0;
	splay(x,0);
}
void Modify(int pos,int tot,int c)
{	
	int x=Findkth(root,pos);
	splay(x,0);
	x=Findkth(root,pos+tot+1);
	splay(x,root);
        a[a[x].l].modi=1;
	a[a[x].l].data=c;
	splay(a[x].l,0);
}
void Reverse(int pos,int tot)
{	
	int x=Findkth(root,pos);
	splay(x,0);
	x=Findkth(root,pos+tot+1);
	splay(x,root);
        a[a[x].l].rev^=1;
	splay(a[x].l,0);
}
int Getsum(int pos,int tot)
{	
	int x=Findkth(root,pos);
	splay(x,0);
	x=Findkth(root,pos+tot+1);
	splay(x,root);
	Push_down(a[x].l);
        return a[a[x].l].sum;
}
int Getmaxsum()
{
	splay(1,0);
	splay(2,root);
	Push_down(a[2].l);
	return a[a[2].l].maxm;
}
int main()
{
        scanf("%d%d",&n,&m);
        f[1]=f[2]=-maxx;
	a[0].maxl=a[0].maxr=a[0].maxm=-maxx;   //3
	a[0].size=a[0].sum=0;
	Build(root,0,1,2);
	Push_up(root);
	for (int i=1;i<=n;i++)
		scanf("%d",&f[i]);
	Build(a[2].l,2,1,n);
	splay(a[2].l,0);
	while (m--)
	{
		scanf("%s",str);
		int q=judge(str);
		if (q==1)
		{
			scanf("%d%d",&pos,&tot);
			for (int i=1;i<=tot;i++)
				scanf("%d",&f[i]);
			Insert(pos+1,tot);
		}
		else if (q==2)
		{
			scanf("%d%d",&pos,&tot);
			Delet(pos,tot);
		}
		else if (q==3)
		{
			int c;
			scanf("%d%d%d",&pos,&tot,&c);
			Modify(pos,tot,c);
		}
		else if (q==4)
		{
			scanf("%d%d",&pos,&tot);
			Reverse(pos,tot);
		}
		else if (q==5)
		{
			scanf("%d%d",&pos,&tot);
			printf("%d\n",Getsum(pos,tot));
		}
		else if (q==6)
		{
			printf("%d\n",Getmaxsum());
		}
	}
	return 0;
}


ac过程好艰辛。。


注释:

1.之前写过的zigzag都是Push_down(y),Push_down(x)。这里为什么要这样做?因为在维护y结点时需要用到y的儿子结点的信息,所以此时必须先把y的儿子结点维护好

(其他题目也可以这样写,这种写法要注意的是在push_down和push_up中写:if (!x) return;)


2.本题读入数据有4000000个数字,按照常规方法a[]要开到4000000,会MLE。但是我们注意到每次Delete之后就会有一些结点标号可以重复利用,因此用s[]表示当前有哪些结点可以重复使用(即内存池),tot2表示s[]的大小;每次New_node()的时候先看内存池中有没有结点标号可以用,如果有就用原来的,否则再用新的(++tot1)。这样就不会超空间了。


3.因为维护结点信息的操作中要用到a[x].l,a[x].r的信息,如果没有左儿子或右儿子就会指向0结点,所以0结点的信息要求对其他结点的信息没有干扰,所以赋值为最小。同时注意,在Push_down() Push_up()操作中,如果x==0,立即返回,不需要维护,否则会出错。



感悟:

1.当前结点的信息被更新后,他的父亲,祖父,曾祖父。。的信息都需要被修改,只要把该结点旋到根结点就可以实现这个目标。

(直接写Push_down()Push_up()也可以,但是容易出错)


2.每次在用到儿子结点的信息时,都要把父亲结点的信息下传;

每次儿子结点的信息被更新后,要用到父亲结点的信息,都要把儿子结点的信息上传。


3.splay(x)之后,x在旋转过程中经过的点及其儿子的信息就都被维护好了




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值