bzoj-1500 维修数列

141 篇文章 0 订阅
88 篇文章 0 订阅

题意:

给出一个长度为n的数列和m个操作;

对每个5,6操作输出一个答案;

被bz吃掉的数据范围:

你可以认为在任何时刻,数列中至少有1个数;
输入数据一定是正确的,即指定位置的数在数列中一定存在;
50%的数据中,任何时刻数列中最多含有30 000个数;
100%的数据中,任何时刻数列中最多含有500 000个数;
100%的数据中,任何时刻数列中任何一个数字均在[-1 000, 1 000]内;
100%的数据中,M ≤20 000,插入的数字总数不超过4 000 000个。


题解:

这是一道好题(笑);

首先分析一下题目;

1,2操作涉及到区间的加入删除,显然是一个splay;

3,4操作是区间的更改,对这样的大数据范围要维护延时标记;

5,6操作是输出,没啥说的;


再看数据范围,数列长度不超过500 000,插入个数不超过4 000 000;

我个蒟蒻居然直接开大数组就搞了,并没有考虑内存回收的问题;

然后WA了半天并且并不能对拍出错;

所以数组开小为什么不是RE啊!!

我开大了5倍内存在cena上A了。。。

↑所以内存要回收↑


为了方便一点去对两边取区间,我在两端加了两个无用结点;

维护6询问就是维护左面的最大值,右面的最大值和中间的最大值,分别用lm,rm,ma表示咯;

但是因为两个无用结点,让问题变得有些复杂;

1.如果无用结点值为0,那么当整个区间都为负数时答案为0,而题目要求必须取一个最大的负数;

2.如果无用结点值为负无穷,在维护sum的时候可能会出问题,并且int内负无穷加着加着就变成正无穷了(笑);

所以我记录了一个ign的标记,赋给了两个无用结点和0结点,在求最大值的时候无视他们的值;


然后说说标记的问题;

3操作小心将区间维护为0,如果仅用判断标记是否为0就会挂在这;

4操作注意一下Pushdown的时候当前结点的左右子树已经交换了,应当交换的是子树的左右子树;

交换时直接交换左右子树的标号和lm与rm即可;


别的没什么说的了,splay实现我开了结构体,不过无所谓啦;

实际上开结构体除了回收内存直接memset方便一点以外都是比较麻烦的;

附赠对拍用数据生成器一个(下面);


代码:


#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 510000
#define lson tr[tr[x].ch[0]]
#define rson tr[tr[x].ch[1]]
#define which(x) (tr[tr[x].fa].ch[1]==x)
using namespace std;
struct splay_tree
{
	int val,ch[2],fa,size,sum,lm,rm,ma,cov;
	bool flag1,flag2,ign;
}tr[N];
char str[100];
int root,tot,st[N],top;
void Pushup(int x)
{
	tr[x].sum=lson.sum+rson.sum+tr[x].val;
	tr[x].size=lson.size+rson.size+1;
	tr[x].lm=max(lson.lm,lson.sum+tr[x].val+max(rson.lm,0));
	tr[x].rm=max(rson.rm,tr[x].val+rson.sum+max(lson.rm,0));
	tr[x].ma=max(max(lson.rm,0)+(tr[x].ign?-0x3f3f3f3f:tr[x].val)+max(rson.lm,0),max(lson.ma,rson.ma));
}
void Pushdown(int x)
{
	if(tr[x].flag2)
	{
		if(tr[x].ch[0])
		{
			lson.flag2=1;
			lson.val=lson.cov=tr[x].cov;
			lson.sum=lson.size*tr[x].cov;
			lson.lm=lson.rm=lson.ma=max(lson.cov,lson.sum);
		}
		if(tr[x].ch[1])
		{
			rson.flag2=1;
			rson.val=rson.cov=tr[x].cov;
			rson.sum=rson.size*tr[x].cov;
			rson.lm=rson.rm=rson.ma=max(rson.cov,rson.sum);
		}
	}
	else if(tr[x].flag1)
	{
		lson.flag1^=1,rson.flag1^=1;
		swap(lson.ch[0],lson.ch[1]);
		swap(lson.lm,lson.rm);
		swap(rson.ch[0],rson.ch[1]);
		swap(rson.lm,rson.rm);
	}
	tr[x].flag1=tr[x].flag2=tr[x].cov=0;
}
int Build(int l,int r,int f)
{
	if(l>r)	return 0;
	int mid=(l+r)>>1,x;
	if(top)
		x=st[top--];
	else
		x=++tot;
	tr[x].fa=f;
	tr[x].ch[0]=Build(l,mid-1,x);
	scanf("%d",&tr[x].val);
	tr[x].ch[1]=Build(mid+1,r,x);
	if(l==r)
	{
		tr[x].size=1;
		tr[x].sum=tr[x].val;
		tr[x].lm=tr[x].rm=tr[x].ma=tr[x].val;
	}
	else
		Pushup(x);
	return x;
}
void Rotate(int x)
{
	int f=tr[x].fa;
	bool k=which(x);
	tr[f].ch[k]=tr[x].ch[!k];
	tr[tr[x].ch[!k]].fa=f;
	tr[x].ch[!k]=f;
	tr[tr[f].fa].ch[which(f)]=x;
	tr[x].fa=tr[f].fa;
	tr[f].fa=x;
	Pushup(f);
	Pushup(x);
}
void Splay(int x,int g)
{
	if(!x)	return ;
	while(tr[x].fa!=g)
	{
		int f=tr[x].fa;
		if(tr[f].fa==g)
		{
			Rotate(x);
			continue;
		}
		if(which(f)^which(x))
			Rotate(x);
		else
			Rotate(f);
		Rotate(x);
	}
	if(!g)	root=x;
}
int Rank(int x,int k)
{
	if(x==0||k==0)	return 0;
	Pushdown(x);
	if(k<=lson.size)
		return Rank(tr[x].ch[0],k);
	else if(k==lson.size+1)
		return x;
	else
		return Rank(tr[x].ch[1],k-lson.size-1);
}
void Init(int n)
{
	int x;
	x=Rank(root,1);
	Splay(x,0);
	tr[x].ch[0]=++tot;
	tr[tot].ign=tr[tot].size=1;
	tr[tot].fa=x;
	tr[tot].lm=tr[tot].rm=tr[tot].ma=-0x3f3f3f3f;
	Pushup(x);
	x=Rank(root,1+n);
	Splay(x,0);
	tr[x].ch[1]=++tot;
	tr[tot].ign=tr[tot].size=1;
	tr[tot].fa=x;
	tr[tot].lm=tr[tot].rm=tr[tot].ma=-0x3f3f3f3f;
	Pushup(x);
}
void Getback(int x)
{
	if(!x)	return ;
	st[++top]=x;
	Getback(tr[x].ch[0]);
	Getback(tr[x].ch[1]);
	memset(&tr[x],0,sizeof(splay_tree));
}
void Insert()
{
	int p,n,x,y;
	scanf("%d%d",&p,&n);
	p++;
	x=Rank(root,p);
	Splay(x,0);
	y=Rank(root,p+1);
	Splay(y,x);
	tr[y].ch[0]=Build(1,n,y);
	Pushup(y);
	Pushup(x);
}
void Delete()
{
	int p,n,x,y;
	scanf("%d%d",&p,&n);
	p++;
	x=Rank(root,p-1);
	Splay(x,0);
	y=Rank(root,p+n);
	Splay(y,x);
	Getback(tr[y].ch[0]);
	tr[y].ch[0]=0;
	Pushup(y);
	Pushup(x);
}
void Update()
{
	int p,n,v,x,y,t;
	scanf("%d%d%d",&p,&n,&v);
	p++;
	x=Rank(root,p-1);
	Splay(x,0);
	y=Rank(root,p+n);
	Splay(y,x);
	t=tr[y].ch[0];
	tr[t].flag2=1;
	tr[t].val=tr[t].cov=v;
	tr[t].sum=tr[t].size*v;
	tr[t].lm=tr[t].rm=tr[t].ma=max(v,tr[t].sum);
	Pushup(y);
	Pushup(x);
}
void Reverse()
{
	int p,n,x,y,t;
	scanf("%d%d",&p,&n);
	p++;
	x=Rank(root,p-1);
	Splay(x,0);
	y=Rank(root,p+n);
	Splay(y,x);
	t=tr[y].ch[0];
	tr[t].flag1^=1;
	swap(tr[t].ch[0],tr[t].ch[1]);
	swap(tr[t].lm,tr[t].rm);
	Pushup(y);
	Pushup(x);
}
void Getsum()
{
	int p,n,x,y,t;
	scanf("%d%d",&p,&n);
	p++;
	x=Rank(root,p-1);
	Splay(x,0);
	y=Rank(root,p+n);
	Splay(y,x);
	t=tr[y].ch[0];
	printf("%d\n",tr[t].sum);
	Pushup(y);
	Pushup(x);
}
void Getmax()
{
	printf("%d\n",tr[root].ma);
}
int main()
{
	int n,m,i,j,k,x,y;
	scanf("%d%d",&n,&m);
	tr[0].ign=1;
	tr[0].lm=tr[0].rm=tr[0].ma=-0x3f3f3f3f;
	root=Build(1,n,0);
	Init(n);
	for(i=1;i<=m;i++)
	{
		scanf("%s",str);
		switch(str[2])
		{
			case 'S':Insert();	break;
			case 'L':Delete();	break;
			case 'K':Update();	break;
			case 'V':Reverse();	break;
			case 'T':Getsum();	break;
			case 'X':Getmax();
		}
	}
	return 0;
}

数据生成器:


#include<stdio.h>
#include<string.h>
#include<algorithm>
#define Random rand()%2000-1000
using namespace std;
typedef long long ll;
int main()
{
	srand(time(NULL));
	int n=300,m=10;
	int i,j,k,x,y;
	printf("%d %d\n",n,m);
	for(i=1;i<=n;i++)
	printf("%d ",Random);
	puts("");
	for(i=1;i<=m;i++)
	{
		k=rand()%6;
		if(!n)	return 0;
		x=rand()%n+1;
		y=rand()%(n-x+1)+1;
		switch(k)
		{
			case 0:	puts("INSERT");	printf("%d %d ",x,y);
					for(j=1;j<=y;j++)
						printf("%d ",Random);
					puts("");
					n+=y;
			break;
			case 1:	puts("DELETE");	
					printf("%d %d\n",x,y);
					n-=y;
			break;
			case 2:	puts("MAKE-SAME");	printf("%d %d %d\n",x,y,Random);
			break;
			case 3:	puts("REVERSE");	printf("%d %d\n",x,y);
			break;
			case 4:	puts("GET-SUM");	printf("%d %d\n",x,y);
			break;
			case 5:	puts("MAX-SUM");
			break;
		}
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值