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