[NOI2005] 维护数列(fhq-Treap)

【问题描述】

请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏 中的下划线‘ _ ’表示实际输入文件中的空格)

操作编号

输入文件中的格式

说明


1.  插入



INSERT_posi_tot_c1_c2_..._ctot

在当前数列的第 posi 个数字后插入 tot

个数字:c1, c2, …, ctot;若在数列首插

入,则 posi 为 0

2.  删除


DELETE_posi_tot

从当前数列的第 posi 个数字开始连续

删除 tot 个数字

3.  修改


MAKE-SAME_posi_tot_c

将当前数列的第 posi 个数字开始的连

续 tot 个数字统一修改为 c

4.  翻转


REVERSE_posi_tot

取出从当前数列的第 posi 个数字开始

的 tot 个数字,翻转后放入原来的位置

5.  求和


GET-SUM_posi_tot

计算从当前数列开始的第 posi 个数字

开始的 tot 个数字的和并输出

6.  求和最

大的子列


MAX-SUM

求出当前数列中和最大的一段子列,

并输出最大和

【输入格式】

输入文件的第 1 行包含两个数 N 和 M,N 表示初始时数列中数的个数,M表示要进行的操作数目。

第 2 行包含 N 个数字,描述初始时的数列。

以下 M 行,每行一条命令,格式参见问题描述中的表格。

【输出格式】

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

【输入样例】

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

【输出样例】

-1
10
1
10

【样例说明】

初始时,我们拥有数列 2 -6 3 5 1 -5 -3 6 3

执行操作 GET-SUM 5 4,表示求出数列中从第 5 个数开始连续 4 个数字之和,1+(-5)+(-3)+6 = -1:

2     -6     3      5      1     -5    -3     6      3

执行操作 MAX-SUM,表示要求求出当前数列中最大的一段和,应为 3+5+1+(-5)+(-3)+6+3 = 10:

2     -6     3      5      1     -5    -3     6      3

执行操作 INSERT 8 3 -5 7 2,即在数列中第 8 个数字后插入-5 7 2,

2     -6     3      5      1     -5    -3     6     -5     7      2      3

执行操作 DELETE 12 1,表示删除第 12 个数字,即最后一个:

2     -6     3      5      1     -5    -3     6     -5     7      2

执行操作 MAKE-SAME 3 3 2,表示从第 3 个数开始的 3 个数字,统一修改为 2:

2	-6	3	5	1	-5	-3	6	-5	7	2

改为

2	-6	2	2	2	-5	-3	6	-5	7	2

执行操作 REVERSE 3 6,表示取出数列中从第 3 个数开始的连续 6 个数:

2           -6            2             2             2           -5            -3            6            -5            7            2

如上所示的灰色部分 2 2 2 -5 -3 6,翻转后得到 6 -3 -5 2 2 2,并放回原来位置:

2     -6     6     -3     -5     2      2      2     -5     7      2

最后执行 GET-SUM 5 4 和 MAX-SUM,不难得到答案 1 和 10。

2            -6            6            -3            -5           2             2            2             -5           7             2

【评分方法】

本题设有部分分,对于每一个测试点:

  • 如果你的程序能在输出文件正确的位置上打印 GET-SUM 操作的答案,你可以得到该测试点 60%的分数;
  • 如果你的程序能在输出文件正确的位置上打印 MAX-SUM 操作的答案,你可以得到该测试点 40%的分数;
  • 以上两条的分数可以叠加,即如果你的程序正确输出所有 GET-SUM 和MAX-SUM 操作的答案,你可以得到该测试点 100%的分数。

请注意:如果你的程序只能正确处理某一种操作,请确定在输出文件正确的位置上打印结果,即必须为另一种操作留下对应的行,否则我们不保证可以正确评分。

【数据规模和约定】

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

  • 100%的数据中,M ≤20 000,插入的数字总数不超过 4 000 000 个,输入文件大小不超过 20MBytes。

大部分为正常区间操作,和最大子列的求法可以参考山海经的求法,在此不再详细解释。注意最终的ans就是中间序列的最大值,不需要再像山海经那样维护一条类似线
段的东西,其他的部分就是细节了,注意最大值的设置和动态内存的清除(防止MLE)

(为皮皮星dalao的数组splay默哀一秒,除了cogs的256M别的哪个OJ都过不了)
无旋Treap150行

代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define key(x)(x?x->v:0)
#define sz(x)(x?x->s:0)
#define sl(x)(x?x->lmax:0)
#define sr(x)(x?x->rmax:0)
#define sm(x)(x?x->ma:-0x3fffffff)
#define ss(x)(x?x->sum:0)
#define ssr(x)(x?x->rmax:-0x3fffffff)
#define ssl(x)(x?x->lmax:-0x3fffffff)
#define sss(x)(x?x->sum:-0x3fffffff)
#define skey(x)(x?x->v:-0x3fffffff)
using namespace std;
inline int read()
{   char c=getchar();int x=0,y=1;
	while(c<'0'||c>'9'){if(c=='-') y=-1;c=getchar();}
	while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
	return x*y;
}
int n,m,inf=0x3fffffff;
struct Treap
{	int v,r,s,mk,sum,lmax,rmax,ma,se;
	Treap *ch[2];
	Treap(int x):v(x){ch[0]=ch[1]=NULL;s=1;se=-inf;r=rand();sum=lmax=rmax=ma=v;mk=0;}
	void revs(){if(this){mk^=1;swap(ch[0],ch[1]);swap(lmax,rmax);}}
	void addv(int x){if(this){se=x;v=x;sum=x*s;lmax=rmax=ma=max(x,sum);}}
	void dn()
	{	if(this)
		{	if(mk){if(ch[0]) ch[0]->revs(); if(ch[1]) ch[1]->revs();mk=0;}
			if(se!=-inf)
			{ 	if(ch[0]) ch[0]->addv(se);
				if(ch[1]) ch[1]->addv(se);
				se=-inf;
			}
		}
	}
	void mt()
	{	if(this)
		{	s=1+sz(ch[0])+sz(ch[1]);
			sum=v+ss(ch[0])+ss(ch[1]);
		 	lmax=max(ssl(ch[0]),max(ss(ch[0])+v+sl(ch[1]),ss(ch[0])+v));
	        rmax=max(ssr(ch[1]),max(ss(ch[1])+v+sr(ch[0]),ss(ch[1])+v));
	        ma=max(max(sm(ch[0]),sm(ch[1])),max(sr(ch[0])+v+sl(ch[1]),max(max(sr(ch[0])+v,sl(ch[1])+v),v)));
	    }
	}
	~Treap(){if(this->ch[0]) delete this->ch[0];if(this->ch[1]) delete this->ch[1];}
}*root;
typedef pair<Treap*,Treap*> dt;
Treap* merge(Treap* x,Treap* y)
{	if(!x) return y;if(!y) return x;
	if(x->r < y->r){x->dn();x->ch[1]=merge(x->ch[1],y);x->mt();return x;}
	else{y->dn();y->ch[0]=merge(x,y->ch[0]);y->mt();return y;}
}
dt split(Treap* x,int k)
{	if(!x) return dt(NULL,NULL);
	dt y;x->dn();
	if(sz(x->ch[0])>=k) y=split(x->ch[0],k),x->ch[0]=y.second,x->mt(),y.second=x;
	else y=split(x->ch[1],k-sz(x->ch[0])-1),x->ch[1]=y.first,x->mt(),y.first=x;
	return y;
}
Treap* build(int len)
{	Treap* *st=new Treap*[len];Treap *las,*x,*ans;
	int tail=0,tmp;
	for(int i=1;i<=len;i++)
	{	tmp=read();x=new Treap(tmp);
		las=NULL;
		while(tail&&st[tail-1]->r > x->r)
		{
			st[tail-1]->mt();las=st[tail-1];
			st[--tail]=NULL;
		}
		if(tail) st[tail-1]->ch[1]=x;
		x->ch[0]=las;
		st[tail++]=x;
	}
	while(tail) st[--tail]->mt();
	ans=st[0];
	delete []st;
	return ans;
}
void insert(int k,int len)
{	Treap *tmp=build(len);
	dt t2=split(root,k);root=merge(t2.first,merge(tmp,t2.second));
}
void del(int k,int len)
{	dt x,y;
	x=split(root,k-1);y=split(x.second,len);
	root=merge(x.first,y.second);
	delete y.first;y.first=NULL;
}
void reve(int k,int len)
{	dt x,y;
	x=split(root,k-1);y=split(x.second,len);
	y.first->revs();
	root=merge(x.first,merge(y.first,y.second));
}
void updata(int k,int len,int val)
{	dt x,y;
	x=split(root,k-1);y=split(x.second,len);
	y.first->addv(val);
	root=merge(x.first,merge(y.first,y.second));
}
void gsum(int k,int len)
{	dt x,y;
	x=split(root,k-1);y=split(x.second,len);
	printf("%d\n",ss(y.first));
	root=merge(x.first,merge(y.first,y.second));
}
void cmp(Treap* x,Treap* y,Treap* &z)
{	if(z)
	{	z->s=sz(x)+sz(y)+1;
	    z->sum=ss(x)+ss(y)+key(z);
	    z->lmax=max(ssl(x),max(ss(x)+sl(y)+key(z),ss(x)+key(z)));
	    z->rmax=max(ssr(y),max(ss(y)+sr(x)+key(z),ss(y)+key(z)));
	    z->ma=max(max(sm(x),sm(y)),max(sr(x)+sl(y)+key(z),max(sr(x)+key(z),max(sl(y)+key(z),key(z)))));
	}
}
int main()
{	freopen("seq2005.in","r",stdin);
	freopen("seq2005.out","w",stdout);
	scanf("%d%d",&n,&m);
	root=build(n);
	char ord[20];int x,y,z;
	for(int i=1;i<=m;i++)
	{	scanf("%s",ord);
		if(ord[0]=='I'){scanf("%d%d",&x,&y);insert(x,y);}
		if(ord[0]=='D'){scanf("%d%d",&x,&y);del(x,y);}
		if(ord[0]=='M'&&ord[2]=='K'){scanf("%d%d%d",&x,&y,&z);updata(x,y,z);}
		if(ord[0]=='R'){scanf("%d%d",&x,&y);reve(x,y);}
		if(ord[0]=='G'){scanf("%d%d",&x,&y);gsum(x,y);}
		if(ord[0]=='M'&&ord[2]=='X') printf("%d\n",root->ma);
	}
	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值