P2042 [NOI2005]维护数列

题目地址:P2042 [NOI2005]维护数列
题目大意: 给你一个数列,要求支持以下操作

编号名称格式说明
1插入 I N S E R T INSERT INSERT p o s i posi posi t o t tot tot c 1 c 2 . . . c t o t c_1c_2...c_{tot} c1c2...ctot在当前数列的第 p o s i posi posi 个数字后插入 t o t tot tot 个数字: c 1 c 2 . . . c t o t ​ c_1c_2...c_{tot} ​ c1c2...ctot ​$若在数列首插入,则 p o s i p o s posipos posiposi 为 0 0 0
2删除 D E L E T E DELETE DELETE p o s i posi posi t o t tot tot从当前数列的第 p o s i posi posi个数字开始连续删除 t o t tot tot个数字
3修改 M A K E − S A M E MAKE-SAME MAKESAME p o s i posi posi t o t tot tot c c c从当前数列的第 p o s i posi posi个数字开始的连续 t o t tot tot个数字统一修改为 c c c
4翻转 R E V E R S E REVERSE REVERSE p o s i posi posi t o t tot tot取出从当前数列的第 p o s i posi posi 个数字开始的 t o t tot tot个数字,翻转后放入原来的位置
5求和 G E T − S U M GET-SUM GETSUM p o s i posi posi t o t tot tot计算从当前数列的第 p o s i posi posi个数字开始的 t o t tot tot个数字的和并输出
6求最大子列和 M A X − S U M MAX-SUM MAXSUM求出当前数列中和最大的一段子列,并输出最大和

题目分析:
看到操作时对序列的操作,首先想到splay维护,但是看看操作,实在是。。。
所以我用fhqtreap来维护的,代码少了不少,注意题目中说数列最多只有 5 e 5 5e5 5e5但插入的数字量可以达到 4 e 6 4e6 4e6,所以我们可以开一个栈,把删除的元素保存起来下次再用。
还有就是注意在维护最大子段和的时候,由于一个节点的左右儿子是他的左边和右边的序列,因此不要忘记把自己加进去 (应该没有人像我这么蠢) ,其他部分就是fhqTreap的板子,代码里有注释。
题目代码:

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<time.h>
#define mxn 500000
using namespace std;
struct Node{
	int lson,rson;
	int val,size,pri;
	int sum,maxn,maxnl,maxnr,cov;
	bool is_rev,is_cov;
}T[mxn];
int stk[mxn+5],arr[mxn+5],rt,pos,tot,c,sz,Top=mxn;
char opt[10];
int new_node(int v){
	sz=stk[Top--]; 
	T[sz].lson=T[sz].rson=T[sz].is_cov=T[sz].is_rev=0;
	T[sz].size=1;
	T[sz].val=T[sz].sum=T[sz].maxn=v;
	T[sz].maxnl=T[sz].maxnr=max(0,v); 
	T[sz].pri=rand();
	return sz;
}
void del(int x){
	if(!x)return;
	stk[++Top]=x;
	if(T[x].lson)del(T[x].lson);
	if(T[x].rson)del(T[x].rson);
	return;
}
void pushup(int x){
	if(!x)return;
	T[x].size=T[T[x].lson].size+T[T[x].rson].size+1;
	T[x].sum=T[T[x].lson].sum+T[T[x].rson].sum+T[x].val;
	
	T[x].maxn=max(T[x].val,T[T[x].lson].maxnr+T[x].val+T[T[x].rson].maxnl);
	if(T[x].lson)T[x].maxn=max(T[x].maxn,T[T[x].lson].maxn);
	if(T[x].rson)T[x].maxn=max(T[x].maxn,T[T[x].rson].maxn);
	//maxn可能会取到负值,所以这里要判一下左右儿子是否存在
	//而下面的前驱后继取不到负值,所以不用判 
	
	T[x].maxnl=max(max(T[T[x].lson].maxnl,T[T[x].lson].sum+T[x].val+T[T[x].rson].maxnl),0);
	T[x].maxnr=max(max(T[T[x].rson].maxnr,T[T[x].rson].sum+T[x].val+T[T[x].lson].maxnr),0);
}
void reverse(int k){
	if(!k)return;
	swap(T[k].lson,T[k].rson);
	swap(T[k].maxnl,T[k].maxnr);
	T[k].is_rev^=1;
}
void cover(int k,int np){
	if(!k)return;
	T[k].val=T[k].cov=np;
	T[k].sum=T[k].size*np;
	T[k].maxn=max(T[k].sum,T[k].val);
	T[k].maxnl=max(0,max(T[k].sum,T[k].val));
	T[k].maxnr=max(0,max(T[k].sum,T[k].val));
	T[k].is_cov=1;
}
void pushdown(int k){
	if(!k)return;
	if(T[k].is_rev){
		reverse(T[k].lson);
		reverse(T[k].rson);
		T[k].is_rev=0;
	}
	if(T[k].is_cov){
		cover(T[k].lson,T[k].cov);
		cover(T[k].rson,T[k].cov);
		T[k].is_cov=0;
	}
}
void split(int now,int k,int &x,int &y){//split时树的左子树的size==k
	if(!now){x=y=0;return;}
	else{
		pushdown(now);
		if(k<=T[T[now].lson].size)
			y=now,split(T[now].lson,k,x,T[now].lson);
		else x=now,split(T[now].rson,k-T[T[now].lson].size-1,T[now].rson,y);
		pushup(now);
	}
}
int merge(int x,int y){//x<y
	if(!x||!y)return x+y;
	if(T[x].pri<T[y].pri){
		pushdown(x);
		T[x].rson=merge(T[x].rson,y);
		pushup(x);
		return x;
	}
	else{
		pushdown(y);
		T[y].lson=merge(x,T[y].lson);
		pushup(y);
		return y;
	}
}
int build(int l,int r){
	if(l==r)return new_node(arr[l]);
	int mid=(l+r)>>1;
	int x=merge(build(l,mid),build(mid+1,r));
	return x;
}
int main()
{
	srand(time(0));
	int n,m;
	scanf("%d %d",&n,&m);
	for(int i=1;i<=mxn;i++)stk[mxn-i+1]=i;
	for(int i=1;i<=n;i++)
		scanf("%d",&arr[i]);
	rt=merge(rt,build(1,n));
	for(int i=1;i<=m;i++){
		int x,y,z;
		scanf("%s",opt);
		if(opt[0]=='I'){
			scanf("%d %d",&pos,&tot);
			for(int i=1;i<=tot;i++)
				scanf("%d",&arr[i]);
			split(rt,pos,x,y);
			x=merge(x,build(1,tot));
			rt=merge(x,y);
		}
		if(opt[0]=='D'){
			scanf("%d %d",&pos,&tot);
			split(rt,pos-1,x,y);//这个数字也要被删掉所以是pos-1 
			split(y,tot,y,z);
			del(y);
			rt=merge(x,z);
		}
		if(opt[0]=='M'&&opt[2]=='K'){
			scanf("%d %d %d",&pos,&tot,&c);
			split(rt,pos-1,x,y);
			split(y,tot,y,z);
			cover(y,c);
			y=merge(y,z);
			rt=merge(x,y);
		}
		if(opt[0]=='R'){
			scanf("%d %d",&pos,&tot);
			split(rt,pos-1,x,y);
			split(y,tot,y,z);
			reverse(y);
			y=merge(y,z);
			rt=merge(x,y);
		}
		if(opt[0]=='G'){
			scanf("%d %d",&pos,&tot);
			split(rt,pos-1,x,y);
			split(y,tot,y,z);
			printf("%d\n",T[y].sum);
			y=merge(y,z);
			rt=merge(x,y);
		}
		if(opt[0]=='M'&&opt[2]=='X'){
			printf("%d\n",T[rt].maxn);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值