spoj 1470 Another Sequence Problem

本题是练习伸展树操作的绝好题目,写了3天,悲剧。。。

题目链接:http://www.spoj.pl/problems/SEQ2/

我因为y和n的顺序写错,导致TLE了一天。。再次悲剧。。

重要的地方我都有注释:

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAX = 500000 * 2;
const int INF = 1000000000;

inline int max(int a,int b){return a>b?a:b;}
inline int min(int a,int b){return a<b?a:b;}

struct Node
{
	int sum;
	int value;
	int size;
	Node *ch[2];
	Node * pre;
	bool rev;
	bool same;
	int MaxL;
	int MaxR;
	int MaxM;
};


struct Splay
{

Node  data[MAX];
Node *store[MAX];//人工堆栈
Node * null;
Node * root;
int number;
int top;


Node * newNode(int val)
{
	Node * p;
	if(top>0)
	{
		p = store[top--];
	}
	else
	{
		p = &data[number++];
	}
	p->size = 1;
	p->value = val;
	p->sum = val;
	p->rev = p->same = false;
	p->MaxL = p->MaxR = p->MaxM = val;
	p->ch[0] = p->ch[1] = null;
	p->pre = null;
	return p;
}


void init()
{
	number = 0;
	top = 0;
	null = newNode(-INF);
	null->size = null->sum = 0;
	root = newNode(-INF);
	root->ch[1] = newNode(-INF);
	root->ch[1]->pre = root;
	push_up(root);//这里主要是为了更新size,sum的更新并不正确,但是在rotate和splay操作中会正确

}

void push_up(Node * p)
{
	if(p == null)
	{
		return;
	}
	push_down(p);
	push_down(p->ch[0]);//以求和为例,p节点值的更新需要依赖两个字节点,那么这两个字节点的sum要先得到更新,所以要下放
	push_down(p->ch[1]);

	p->size = p->ch[0]->size + p->ch[1]->size + 1;
	p->sum = p->ch[0]->sum + p->ch[1]->sum + p->value;
	p->MaxL = max(p->ch[0]->MaxL,p->ch[0]->sum + p->value + max(0,p->ch[1]->MaxL));
	p->MaxR = max(p->ch[1]->MaxR,p->ch[1]->sum + p->value + max(0,p->ch[0]->MaxR));
    p->MaxM=max(p->ch[0]->MaxM,p->ch[1]->MaxM);
    p->MaxM=max(p->MaxM,max(p->ch[0]->MaxR+p->ch[1]->MaxL,0)+p->value);
    p->MaxM=max(p->MaxM,max(p->ch[0]->MaxR,p->ch[1]->MaxL)+p->value);
}

void push_down(Node * p)
{
	if(p == null)
	{
		return;
	}
	if(p->rev)
	{
		p->rev = false;
		p->ch[0]->rev ^=1;
		p->ch[1]->rev ^=1;
		swap(p->ch[0],p->ch[1]);
		swap(p->MaxL,p->MaxR);
	}
	if(p->same)
	{
		p->same = false;
		p->ch[0]->same = p->ch[1]->same = true;
		p->ch[0]->value = p->ch[1]->value = p->value;
		p->sum = p->value * p->size;
		p->MaxL = p->MaxR = p->MaxM = p->size * p->value;
		if(p->value <0)
		{
			p->MaxL = p->MaxR = p->MaxM = p->value;
		}
	}

}


//中序遍历
void dfs(Node * p)
{
	if(p == null)
	{
		return;
	}
	dfs(p->ch[0]);
	printf("%d   ",p->value);
	dfs(p->ch[1]);
}
//c=1 右旋
void rotate(Node * x,int c)
{
	Node * y = x->pre;
	push_down(y);
	push_down(x);

	y->ch[!c] = x->ch[c];
	if(x->ch[c]!=null)
	{
		x->ch[c]->pre = y;
	}
	x->pre = y->pre;
	if(y->pre !=null)
	{
		if(y->pre->ch[0] == y)
		{
			y->pre->ch[0] = x;
		}
		else
		{
			y->pre->ch[1] = x;
		}
	}
	x->ch[c] = y;
	y->pre = x;
	push_up(y);
	if(y == root)
	{
		root = x;
	}

}
void splay(Node * x,Node * f)
{
	if(x == null)
	{
		return;
	}

	for(push_down(x);x->pre!=f;)
	{
		if(x->pre->pre == f)//单旋
		{
			if(x == x->pre->ch[0])
			{
				rotate(x,1);
			}
			else
			{
				rotate(x,0);
			}
		}
		else
		{
			Node * y = x->pre;
			Node * z = y->pre;
			if(z->ch[0] == y)
			{
				if(y->ch[0] == x)//一字形旋转
				{
					rotate(y,1);
					rotate(x,1);

				}
				else//之字形
				{
					rotate(x,0);
					rotate(x,1);
				}
			}
			else
			{
				if(y->ch[1] == x)//一字形
				{
					rotate(y,0);
					rotate(x,0);
				}
				else//之字形
				{
					rotate(x,1);
					rotate(x,0);
				}
			}

		}
	}
	push_up(x);
}
void select(int k,Node * f)//k是指第几个节点,从1开始
{
	Node * t;
	//k = k-1+1;//其实正常情况下应该是 k = k-1;但是前后加两个节点的话,正好向右移动一位
 	for(t=root;;)
	{
		push_down(t);
		int size = t->ch[0]->size;
		if(k == size)
		{
			break;
 		}
		if(k < size)
		{
			t = t->ch[0];
		}
		else
		{
			k -= size + 1;
			t = t->ch[1];
		}

	}
	splay(t,f);
}

Node * build(int left,int right,int *ary)
{
	if(left>right)
	{
		return null;
	}
	int mid = (left + right)>>1;
	Node * p  =  newNode(ary[mid]);
	p->ch[0] = build(left,mid-1,ary);
	if(p->ch[0]!=null)
	{
		p->ch[0]->pre = p;
	}
	p->ch[1] = build(mid+1,right,ary);
	if(p->ch[1]!=null)
	{
		p->ch[1]->pre = p;
	}
	push_up(p);//Update操作
	return p;
}


void insert(int pos,int *ary,int n)
{
	select(pos-1,null);
	select(pos,root);
	Node * p = build(0,n-1,ary);
	root->ch[1]->ch[0] = p;
	p->pre = root->ch[1];
	splay(p,null);//将p移至根节点
}
void del(int pos,int end)
{
	select(pos-1,null);
	select(end,root);
	Node * p = root->ch[1]->ch[0];//如果不回收的话,将会造成内存泄漏
	root->ch[1]->ch[0] = null;
	splay(root->ch[1],null);//别忘了维护
	recyle(p);
}

void recyle(Node *p)//人工回收删除的空间,防止栈溢出
{
	if(p == null)
	{
		return;
	}
	recyle(p->ch[0]);
	recyle(p->ch[1]);
	store[++top] = p;

}
int getSum(int start,int end)
{
	select(start-1,null);
	select(end,root);//这里完全不用担心,肯定会移到root的右子树,因为end比start-1大,根据查询二叉树的性质
	return root->ch[1]->ch[0]->sum;
}
int maxSum(int pos,int end)
{
	select(pos-1,null);
	select(end,root);
	Node * p = root->ch[1]->ch[0];
	return p->MaxM;
}
void makeSame(int pos,int end,int s)
{
	select(pos-1,null);
	select(end,root);
	Node * p = root->ch[1]->ch[0];
	p->same = true;
	p->value = s;
	splay(p,null);
}
void reverse(int pos,int end)
{
	select(pos-1,null);
	select(end,root);
	Node * p = root->ch[1]->ch[0];
	p->rev ^= 1;
	splay(p,null);
}
}sp;
int ary[MAX];
int main()
{
	int T;

	#ifndef ONLINE_JUDGE
		freopen("in.txt","r",stdin);
	#endif


	for(scanf("%d",&T);T--;)
	{
		int n,m;
		int x,y;
		int s;
		Node * p;
		char cmd[20];

		scanf("%d%d",&n,&m);
		for(int i=0;i<n;i++)
		{
			scanf("%d",&ary[i]);
		}
		sp.init();
		sp.insert(1,ary,n);

		 for(;m--;)
		{
			scanf("%s",cmd);
			if(strcmp(cmd,"GET-SUM") == 0)
			{
				scanf("%d%d",&x,&y);
				printf("%d\n",sp.getSum(x,x+y));
			}
			if(strcmp(cmd,"MAX-SUM") == 0)
			{
				printf("%d\n",sp.maxSum(1,sp.root->size - 1));
			}

			if(strcmp(cmd,"INSERT") == 0)
			{
				scanf("%d%d",&x,&y);
				x++;//向后移动一位
				for(int i=0;i<y;i++)
				{
					scanf("%d",&ary[i]);
				}
				sp.insert(x,ary,y);
			}
			if(strcmp(cmd,"DELETE") == 0)
			{
				scanf("%d%d",&x,&y);
				sp.del(x,x+y);
			}
			if(strcmp(cmd,"REVERSE") == 0)
			{
				scanf("%d%d",&x,&y);
				sp.reverse(x,x+y);
			}
			if(strcmp(cmd,"MAKE-SAME") == 0)
			{
				scanf("%d%d%d",&x,&y,&s);
				sp.makeSame(x,x+y,s);
			}

		}
		//dfs(root);
		//printf("\n");
	}
	return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值