【SPLAY】NOI2005 维护数列

【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 求出当前数列中和最大的一段子列,并输出最大和


【题解】裸的splay题目。


建立一棵Splay,每个节点上要维护一下几个信息:数值key,子树大小size,子树和sum,子数内和最大的子数列 maxsum,子树区间内由左边界能够延伸的和最大子数列lsum,子树区间内由右边界能够延伸的和最大子数列rsum,以及两个标记:子树反转标记rev,子树同化标记same。为了避免判断边界条件,首先插入两个节点,作为开头和结尾,其永久排名分别为1和当前数列长度。


自底向上维护节点信息。


有一些要注意的地方。

标记reverse标记时,假如标记到x节点,只需将x节点的lsum与rsum交换,不需要交换x的左右儿子。

在x节点改变位置或者splay时,要下传标记的时候,才交换x的左右儿子。(代码中的PushDown)

任何时候只要访问到带标记的节点,一定要标记下传,尤其不要忘了在splay旋转的时候。


记住,splay树的性质是:在任何时候树的中序遍历一定是当前的序列。


#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for (int i = a;i <= b;i ++)

using namespace std;

const int maxn = 500005;
int N,M,pos0,posN1,root;
int A[maxn],pool[maxn];

struct NODE
{
	int lsum,rsum,rev,maxsum,sum,key,size,same;
	int c[2],father;
}tree[maxn];

void Initialize()
{
	for (int i = (int)5e5;i;i --) pool[++pool[0]] = i;
	scanf("%d%d",&N,&M);
	fo(i,1,N) scanf("%d",&A[i]);
}

void PushUp(int x)
{
	int l = tree[x].c[0], r = tree[x].c[1];
	if (l>0 && r>0)
	{
		int cl = max(0,tree[l].rsum), cr = max(0,tree[r].lsum);
		tree[x].size = tree[l].size + tree[r].size + 1;
		tree[x].lsum = max(tree[l].lsum,tree[l].sum+tree[x].key+cr);
		tree[x].rsum = max(tree[r].rsum,tree[r].sum+tree[x].key+cl);
		tree[x].maxsum = max(max(tree[l].maxsum,tree[r].maxsum),cl+tree[x].key+cr);
		tree[x].sum = tree[l].sum + tree[r].sum + tree[x].key;
	} else
	if (l>0)
	{
		int cl = max(0,tree[l].rsum);
		tree[x].size = tree[l].size + 1;
		tree[x].lsum = max(tree[l].lsum,tree[l].sum+tree[x].key);
		tree[x].rsum = tree[x].key + cl;
		tree[x].maxsum = max(tree[l].maxsum,tree[x].key+cl);
		tree[x].sum = tree[l].sum + tree[x].key;
	} else
	if (r>0)
	{
		int cr = max(0,tree[r].lsum);
		tree[x].size = tree[r].size + 1;
		tree[x].rsum = max(tree[r].rsum,tree[r].sum+tree[x].key);
		tree[x].lsum = tree[x].key + cr;
		tree[x].maxsum = max(tree[r].maxsum,tree[x].key+cr);
		tree[x].sum = tree[r].sum + tree[x].key;
	} else
	{
		tree[x].size = 1;
		tree[x].lsum = tree[x].rsum = tree[x].maxsum = tree[x].sum = tree[x].key;
	}
}

void PutRev(int x)
{
	if (x<0) return;
	tree[x].rev ^= 1;
	swap(tree[x].lsum,tree[x].rsum);
}

void PutSame(int x,int v)
{
	if (x<0) return;
	tree[x].key = tree[x].same = v;
	tree[x].sum = v * tree[x].size;
	tree[x].lsum = tree[x].rsum = tree[x].maxsum = max(tree[x].key,tree[x].sum);
}

void PushDown(int x)
{
	if (tree[x].rev)
	{
		PutRev(tree[x].c[0]);
		PutRev(tree[x].c[1]);
		swap(tree[x].c[0],tree[x].c[1]);
		tree[x].rev = 0;
	}
	if (tree[x].same != maxn)
	{
		PutSame(tree[x].c[0],tree[x].same);
		PutSame(tree[x].c[1],tree[x].same);
		tree[x].same = maxn;
	}
}

int MakeSplay(int l,int r,int father)
{
	int mid = (l + r) >> 1;
	int pos = pool[pool[0]--];
	tree[pos].size = 1;
	tree[pos].c[0] = tree[pos].c[1] = -maxn;
	tree[pos].same = maxn;
	tree[pos].rev = 0;
	tree[pos].key = A[mid];
	tree[pos].father = father;
	if (mid == 0) pos0 = pos;
	if (mid == N+1) posN1 = pos;
	if (l < mid) tree[pos].c[0] = MakeSplay(l,mid-1,pos);
	if (mid < r) tree[pos].c[1] = MakeSplay(mid+1,r,pos);
	PushUp(pos);
	return pos;
}

void rotate(int x,int d)
{
	int y = tree[x].c[d];
	int z = tree[y].c[!d];
	if (tree[x].father > 0)
	{
		int t = tree[x].father;
		int r = (tree[t].c[1] == x);
		tree[t].c[r] = y;
	}
	if (z > 0) tree[z].father = x;
	tree[x].c[d] = z;
	tree[y].father = tree[x].father;
	tree[x].father = y; tree[y].c[!d] = x;
	PushUp(x);
	PushUp(y);
}

int tank[maxn],dir[maxn];
void Splay(int x,int t)
{
	int cnt = 0;
	for (int i = x;i != tree[t].father;i = tree[i].father) tank[++cnt] = i;
	for (int i = cnt;i;i --) PushDown(tank[i]);
	fo(i,2,cnt) dir[i] = (tree[tank[i]].c[1] == tank[i-1]);
	for (int i = 2;i <= cnt;i += 2)
		if (i == cnt) rotate(tank[i],dir[i]);
		else
		{
			if (dir[i] == dir[i+1]) rotate(tank[i+1],dir[i+1]), rotate(tank[i],dir[i]);
			else rotate(tank[i],dir[i]), rotate(tank[i+1],dir[i+1]);
		}
	if (t == root) root = x;
}

int Select(int x,int v)
{
	int cur;
	PushDown(x);
	if (tree[x].c[0] > 0) cur = tree[tree[x].c[0]].size + 1; else cur = 1;
	if (cur == v) return x;
	if (v <= cur) return Select(tree[x].c[0],v);
	else return Select(tree[x].c[1],v-cur);
}

void INSERT()
{
	int posi,ctot;
	scanf("%d%d",&posi,&ctot);
	int pos = Select(root,posi+1), posnext = Select(root,posi+2);
	Splay(posnext,root);
	Splay(pos,tree[posnext].c[0]);
	fo(i,1,ctot) scanf("%d",&A[i]);
	tree[tree[root].c[0]].c[1] = MakeSplay(1,ctot,tree[root].c[0]);
	PushUp(tree[root].c[0]),PushUp(root);
}

void Dispose(int x)
{
	if (x<0) return;
	Dispose(tree[x].c[0]);
	Dispose(tree[x].c[1]);
	memset(&tree[x],0,sizeof tree[x]);
	pool[++pool[0]] = x;
}

int l,r,c,prel,sucr;
inline void Set(int l,int r)
{
	r = l + r - 1;
	int prel = Select(root,l), sucr = Select(root,r+2);
	Splay(sucr,root);
	Splay(prel,tree[sucr].c[0]);
}

void DELETE()
{
	scanf("%d%d",&l,&r);
	Set(l,r);
	int x = tree[root].c[0];
	Dispose(tree[x].c[1]);
	tree[x].c[1] = -maxn;
	PushUp(x),PushUp(root);
}

void MAKESAME()
{
	scanf("%d%d%d",&l,&r,&c);
	Set(l,r);
	int x = tree[root].c[0];
	PutSame(tree[x].c[1],c);
	PushUp(x),PushUp(root);
}

void REVERSE()
{
	scanf("%d%d",&l,&r);
	Set(l,r);
	int x = tree[root].c[0];
	PutRev(tree[x].c[1]);
	PushUp(x),PushUp(root);
}

void GETSUM(int cs)
{
	scanf("%d%d",&l,&r);
	if (!r) 
	{
		printf("0\n");
		return;
	}
	Set(l,r);
	int x = tree[root].c[0];
	printf("%d\n",tree[tree[x].c[1]].sum);
}

void MAXSUM()
{
	Splay(posN1,root);
	Splay(pos0,tree[root].c[0]);
	printf("%d\n",tree[tree[tree[root].c[0]].c[1]].maxsum);
}

void Work()
{
	root = MakeSplay(0,N+1,-1);
	fo(cs,1,M)
	{
		char op[10];
		scanf("%s",op);
		string s(op);
		if (s == "INSERT") INSERT();
		if (s == "DELETE") DELETE();
		if (s == "MAKE-SAME") MAKESAME();
		if (s == "REVERSE") REVERSE();
		if (s == "GET-SUM") GETSUM(cs);
		if (s == "MAX-SUM") MAXSUM();
	}
}

int main()
{
	Initialize();
	Work();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值