noi2005维护数列

请写一个程序,要求维护一个数列,支持以下 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。


splay维护数列模板题,我们认为该splay的中序遍历即为原数列,那么处理区间只需将该区间左边界点左相邻点旋转至树根,将区间右边界点右相邻点旋转至树根右儿子,则树根右儿子的左儿子的子树即为所要处理的区间,

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <string>
using namespace std;
string s;
const int maxn=500000+10;
queue<int>q;
const int INF=0x7fffffff;
int tr[maxn][3];
int root;
int rotated[maxn]={0};
long long summ[maxn]={0};
long long maxx[maxn]={0};
int fa[maxn];
int n;
int id[maxn];
int a[maxn];
int v[maxn];
long long lmax[maxn];
long long rmax[maxn];
int size[maxn];
int cnt;
int tag[maxn];
inline void pushup(int x){
	int l=tr[x][0],r=tr[x][1];
	summ[x]=summ[l]+summ[r]+v[x];
	size[x]=size[l]+size[r]+1;
	maxx[x]=max(maxx[l],maxx[r]);
	maxx[x]=max(maxx[x],rmax[l]+v[x]+lmax[r]);
	lmax[x]=max(lmax[l],summ[l]+v[x]+lmax[r]);
	rmax[x]=max(rmax[r],summ[r]+v[x]+rmax[l]);
}
inline void pushdown(int x){
	int l=tr[x][0],r=tr[x][1];
	if(tag[x]){
		rotated[x]=tag[x]=0;
		if(l)
			tag[l]=1,v[l]=v[x],summ[l]=v[x]*size[l];
		if(r)
			tag[r]=1,v[r]=v[x],summ[r]=v[x]*size[r];
		if(v[x]>=0){
			if(l)
				lmax[l]=rmax[l]=maxx[l]=summ[l];
			if(r)
				lmax[r]=rmax[r]=maxx[r]=summ[r];
		}
		else {
			if(l)
				lmax[l]=rmax[l]=0,maxx[l]=v[x];
			if(r)
				lmax[r]=rmax[r]=0,maxx[r]=v[x];
		}
	}
	if(rotated[x]){
		swap(tr[l][0],tr[l][1]);
		swap(tr[r][0],tr[r][1]);
		swap(lmax[l],rmax[l]);
		swap(lmax[r],rmax[r]);
		rotated[l]^=1;
		rotated[r]^=1;
		rotated[x]^=1;
	}
}
inline void rotate(int x,int& o){
	int l,r;
	int y,z;
	y=fa[x],z=fa[y];
	l=(x==tr[y][1]);
	r=1-l;
	if(y==o)
		o=x;
	else tr[z][tr[z][1]==y]=x;
	fa[tr[x][r]]=y;
	fa[y]=x;
	fa[x]=z;
	tr[y][l]=tr[x][r];
	tr[x][r]=y;
	pushup(y);
	pushup(x);
}
inline void splay(int x,int& k){
	int y,z;
	while(x!=k){
		y=fa[x],z=fa[y];
		if(y!=k){
			if((y==tr[z][1])^(x==tr[y][1]))
				rotate(x,k);
			else rotate(y,k);
		}
		rotate(x,k);
	}
}
inline int find(int o,int k){
	pushdown(o);
	int l=tr[o][0],r=tr[o][1];
	if(size[l]+1==k)
		return o;
	else if(size[l]>=k)
		return find(l,k);
	else return find(r,k-size[l]-1);
}
inline void dec(int x){
	if(!x)
		return;
	int l=tr[x][0],r=tr[x][1];
	dec(l);
	dec(r);
	q.push(x);
	fa[x]=tr[x][0]=tr[x][1]=0;
	tag[x]=rotated[x]=0;
}
inline int spilt(int o,int tot){
	int x=find(root,o),y=find(root,o+tot+1);
	splay(x,root);
	splay(y,tr[x][1]);
	return tr[y][0];
} 
inline void Sum(int o,int k){
	int x=spilt(o,k);
	printf("%lld\n",summ[x]);
}
inline void modify(int k,int tot,int val){
	int x=spilt(k,tot),y=fa[x];
	v[x]=val;
	tag[x]=1;
	summ[x]=size[x]*val;
	if(val>=0)
		lmax[x]=rmax[x]=maxx[x]=summ[x];
	else lmax[x]=rmax[x]=0,maxx[x]=val;
	pushup(y);
	pushup(fa[y]);
}
inline void rev(int o,int k){
	int x=spilt(o,k);
	int y=fa[x];
	if(!tag[x]){
		swap(lmax[x],rmax[x]);
		swap(tr[x][0],tr[x][1]);
		rotated[x]^=1;
		pushup(y);
		pushup(fa[y]);
	}
}
inline void del(int o,int k){
	int x=spilt(o,k);
	int y=fa[x];
	dec(x);
	tr[y][0]=0;
	pushup(y);
	pushup(fa[y]);
}
inline void build(int l,int r,int f){
	if(l>r)
		return;
	int mid=(l+r)>>1,now=id[mid],last=id[f];
	if(l==r){
		summ[now]=a[l];
		size[now]=1;
		tag[now]=0;
		rotated[now]=0;
		if(a[l]>=0)
			lmax[now]=rmax[now]=maxx[now]=a[l];
		else lmax[now]=rmax[now]=0,maxx[now]=a[l];
	}
	else {
		build(l,mid-1,mid);
		build(mid+1,r,mid);
	}
	v[now]=a[mid];
	fa[now]=last;
	pushup(now);
	tr[last][mid>=f]=now;
}
inline void insert(int k,int tot){
	for(int i=1;i<=tot;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=tot;i++)
		if(!q.empty())
			id[i]=q.front(),q.pop();
		else id[i]=++cnt;
	build(1,tot,0);
	int z=id[(1+tot)>>1];
	int x=find(root,k+1),y=find(root,k+2);
	splay(x,root);
	splay(y,tr[x][1]);
	fa[z]=y;
	tr[y][0]=z;
	pushup(y);
	pushup(x);
}
inline void dfs(int x){
	pushdown(x);
	if(tr[x][0])
		dfs(tr[x][0]);
	printf("%d ",v[x]);
	if(tr[x][1])
		dfs(tr[x][1]);
}
int main(){
	freopen("seq2005.in","r",stdin);
	freopen("seq2005.out","w",stdout);
	int m;
	scanf("%d %d",&n,&m);
	maxx[0]=a[1]=a[n+2]=-INF;
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i+1]);
	for(int i=1;i<=n+2;i++)
		id[i]=i;
	build(1,n+2,0);
	root=(n+3)>>1;
	cnt=n+2;
	int k,tot,val;
	while(m--){
		cin>>s;
		if(s[0]!='M'||s[2]!='X')
			scanf("%d %d",&k,&tot);
		if(s[0]=='I')
			insert(k,tot);
		if(s[0]=='D')
			del(k,tot);
		if(s[0]=='M'){
			if(s[2]=='X')
				printf("%d\n",maxx[root]);
			else scanf("%d",&val),modify(k,tot,val);
		}
		if(s[0]=='R')
			rev(k,tot);
		if(s[0]=='G')
			Sum(k,tot);
		//dfs(root);
		//printf("\n");
	}
return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值