本题是练习伸展树操作的绝好题目,写了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;
}