【NOI 2005】维修数列 Sequence
<题目描述自行搜索……>
为了学习LCT,要练好Splay。这道题的插入、修改、翻转操作都是裸的Splay操作,不怎么难,关键是最大子段和的维护上。之前做过一道线段树的题叫《小白逛公园》,是要用一棵线段树来维护最大子段和,然后我就把线段树的维护方法加到了Splay上,写了一晚上,总算是A掉了……
维护最大字段和的方法:
我们先维护一个序列的包括最左端节点的最大子段和MaxStartLeft,维护一个包括其最右端节点的最大字段和MaxStartRight,然后再维护每个节点左子树的Sum和右子树的Sum。那么对于每个节点,以他为根的子树的序列的最大子段和就有如下几种情况:
1.节点本身
2.左子树中最大子段和
3.右子树中最大子段和
4.左子树的MaxStartRight+节点本身
5.右子树的MaxStartLeft+节点本身
6.左子树的MaxStartRight+节点本身+右子树的MaxStartLeft
然后每次Update的时候维护一下就好了。
虽然是A了,但是在OJ上是交不过的,因为没有动态释放内存,内存巨大……
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #define NIL SPLAY #define MN 3000000 using namespace std; template<class T>inline void gmax(T &a,T b){if(a<b)a=b;} const int INF=100000; int n,m,pos,num,x,tmp[500000]; char s[10]; struct SPLAYTREE{ struct NODE{ int key,sum,maxsum,mls,mrs,size; bool rev,same; NODE *left,*right,*father; NODE (){} NODE(int _key):key(_key){ maxsum=mrs=mls=sum=key; size=1;rev=same=false; } }SPLAY[MN],*SP,*root,*head,*tail; void NewNode(NODE *&t,int key){ t=new(++SP)NODE(key); t->left=t->right=t->father=NIL; } void init(){ SP=NIL; NIL->key=NIL->maxsum=NIL->mls=NIL->mrs=-INF,NIL->sum=NIL->size=0; NIL->left=NIL->right=NIL->father=NIL; NewNode(head,-INF); NewNode(tail,-INF); head->sum=tail->sum=0; head->right=tail,tail->father=head,head->size++; root=head; } void pushdown(NODE *&t){ if(t->rev){ swap(t->left,t->right); swap(t->mls,t->mrs); t->left->rev=!t->left->rev,t->right->rev=!t->right->rev; t->rev=false; } if(t->same){ t->same=false; t->left->same=t->right->same=true; t->left->key=t->right->key=t->key; t->mls=t->mrs=t->sum=t->maxsum=t->key*t->size; if(t->key<0) t->mls=t->mrs=t->maxsum=t->key; } } void update(NODE *&t){ t->size=t->left->size+t->right->size+1; t->sum=t->left->sum+t->right->sum+t->key; t->mls=t->left->mls; gmax(t->mls,t->left->sum+t->key); gmax(t->mls,t->left->sum+t->key+t->right->mls); t->mrs=t->right->mrs; gmax(t->mrs,t->right->sum+t->key); gmax(t->mrs,t->right->sum+t->key+t->left->mrs); t->maxsum=t->key; gmax(t->maxsum,t->left->maxsum); gmax(t->maxsum,t->right->maxsum); gmax(t->maxsum,t->left->mrs+t->key); gmax(t->maxsum,t->right->mls+t->key); gmax(t->maxsum,t->left->mrs+t->key+t->right->mls); } void zig(NODE *&t){ NODE *f=t->father,*r=t->right; pushdown(f->right); pushdown(t->left); pushdown(t->right); t->father=f->father; if(f==root) root=t; else{ if(f->father->left==f) f->father->left=t; else f->father->right=t; } t->right=f,f->father=t,f->left=r,r->father=f; update(f);update(t); } void zag(NODE *&t){ NODE *f=t->father,*l=t->left; pushdown(f->left); pushdown(t->left); pushdown(t->right); t->father=f->father; if(f==root) root=t; else{ if(f->father->right==f) f->father->right=t; else f->father->left=t; } t->left=f,f->father=t,f->right=l,l->father=f; update(f);update(t); } void splay(NODE *&root,NODE *&t){ pushdown(t); while(root!=t){ if(t->father==root){ if(t->father->left==t) zig(t); else zag(t); }else{ if(t->father->father->left==t->father){ if(t->father->left==t) zig(t->father),zig(t); else zag(t),zig(t); }else{ if(t->father->right==t) zag(t->father),zag(t); else zig(t),zag(t); } } } } void select(NODE *&root,int pos){ NODE *r=root; while(pushdown(r),r->left->size+1!=pos){ if(r->left->size+1>pos) r=r->left; else pos-=r->left->size+1,r=r->right; } splay(root,r); } void insert(int pos,int num){ NODE *t,*p,*q; NewNode(t,tmp[1]);p=q=t; for(int i=2;i<=num;i++){ NewNode(t,tmp[i]); t->father=p; p=p->right=t; } select(root,pos); select(root->right,1); root->right->left=q; q->father=root->right; splay(root,p); } void Delete(int pos,int num){ select(root,pos); select(root->right,num+1); NODE *t=root->right; t->left=NIL; splay(root,t); } void Make_Same(int pos,int num,int key){ select(root,pos); select(root->right,num+1); NODE *t=root->right->left; t->key=key,t->same=true; splay(root,t); } void Reverse(int pos,int num){ select(root,pos); select(root->right,num+1); NODE *t=root->right->left; t->rev=!t->rev; splay(root,t); } int Get_Sum(int pos,int num){ select(root,pos); select(root->right,num+1); return root->right->left->sum; } void Max_Sum(){printf("%d\n",root->maxsum);} }tree; int main(){ scanf("%d%d",&n,&m); tree.init(); for(int i=1;i<=n;i++) scanf("%d",&tmp[i]); tree.insert(1,n); while(m--){ scanf("%s",s); switch(s[0]){ case 'I': scanf("%d%d",&pos,&num); if(!num) break; for(int i=1;i<=num;i++) scanf("%d",&tmp[i]); tree.insert(pos+1,num); break; case 'D': scanf("%d%d",&pos,&num); tree.Delete(pos,num); break; case 'R': scanf("%d%d",&pos,&num); tree.Reverse(pos,num); break; case 'G': scanf("%d%d",&pos,&num); printf("%d\n",tree.Get_Sum(pos,num)); break; case 'M': if(s[2]=='K'){ scanf("%d%d%d",&pos,&num,&x); tree.Make_Same(pos,num,x); break; }else tree.Max_Sum(); } } return 0; }
然后我发现了一个很神奇的事情,改动程序里的一个小地方能提高我的程序3s……
那就是在zig(t)和zag(t)的时候,我们不update(t),而是在splay()中更新t,这样就减少了更新次数……
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #define NIL SPLAY #define MN 3000000 using namespace std; template<class T>inline void gmax(T &a,T b){if(a<b)a=b;} const int INF=100000; int n,m,pos,num,x,tmp[500000]; char s[10]; struct SPLAYTREE{ struct NODE{ int key,sum,maxsum,mls,mrs,size; bool rev,same; NODE *left,*right,*father; NODE (){} NODE(int _key):key(_key){ maxsum=mrs=mls=sum=key; size=1;rev=same=false; } }SPLAY[MN],*SP,*root,*head,*tail; void NewNode(NODE *&t,int key){ t=new(++SP)NODE(key); t->left=t->right=t->father=NIL; } void init(){ SP=NIL; NIL->key=NIL->maxsum=NIL->mls=NIL->mrs=-INF,NIL->sum=NIL->size=0; NIL->left=NIL->right=NIL->father=NIL; NewNode(head,-INF); NewNode(tail,-INF); head->sum=tail->sum=0; head->right=tail,tail->father=head,head->size++; root=head; } void pushdown(NODE *&t){ if(t->rev){ swap(t->left,t->right); swap(t->mls,t->mrs); t->left->rev=!t->left->rev,t->right->rev=!t->right->rev; t->rev=false; } if(t->same){ t->same=false; t->left->same=t->right->same=true; t->left->key=t->right->key=t->key; t->mls=t->mrs=t->sum=t->maxsum=t->key*t->size; if(t->key<0) t->mls=t->mrs=t->maxsum=t->key; } } void update(NODE *&t){ t->size=t->left->size+t->right->size+1; t->sum=t->left->sum+t->right->sum+t->key; t->mls=t->left->mls; gmax(t->mls,t->left->sum+t->key); gmax(t->mls,t->left->sum+t->key+t->right->mls); t->mrs=t->right->mrs; gmax(t->mrs,t->right->sum+t->key); gmax(t->mrs,t->right->sum+t->key+t->left->mrs); t->maxsum=t->key; gmax(t->maxsum,t->left->maxsum); gmax(t->maxsum,t->right->maxsum); gmax(t->maxsum,t->left->mrs+t->key); gmax(t->maxsum,t->right->mls+t->key); gmax(t->maxsum,t->left->mrs+t->key+t->right->mls); } void zig(NODE *&t){ NODE *f=t->father,*r=t->right; pushdown(f->right); pushdown(t->left); pushdown(t->right); t->father=f->father; if(f==root) root=t; else{ if(f->father->left==f) f->father->left=t; else f->father->right=t; } t->right=f,f->father=t,f->left=r,r->father=f; update(f); } void zag(NODE *&t){ NODE *f=t->father,*l=t->left; pushdown(f->left); pushdown(t->left); pushdown(t->right); t->father=f->father; if(f==root) root=t; else{ if(f->father->right==f) f->father->right=t; else f->father->left=t; } t->left=f,f->father=t,f->right=l,l->father=f; update(f); } void splay(NODE *&root,NODE *&t){ pushdown(t); while(root!=t){ if(t->father==root){ if(t->father->left==t) zig(t); else zag(t); }else{ if(t->father->father->left==t->father){ if(t->father->left==t) zig(t->father),zig(t); else zag(t),zig(t); }else{ if(t->father->right==t) zag(t->father),zag(t); else zig(t),zag(t); } } } update(t); } void select(NODE *&root,int pos){ NODE *r=root; while(pushdown(r),r->left->size+1!=pos){ if(r->left->size+1>pos) r=r->left; else pos-=r->left->size+1,r=r->right; } splay(root,r); } void insert(int pos,int num){ NODE *t,*p,*q; NewNode(t,tmp[1]);p=q=t; for(int i=2;i<=num;i++){ NewNode(t,tmp[i]); t->father=p; p=p->right=t; } select(root,pos); select(root->right,1); root->right->left=q; q->father=root->right; splay(root,p); } void Delete(int pos,int num){ select(root,pos); select(root->right,num+1); NODE *t=root->right; t->left=NIL; splay(root,t); } void Make_Same(int pos,int num,int key){ select(root,pos); select(root->right,num+1); NODE *t=root->right->left; t->key=key,t->same=true; splay(root,t); } void Reverse(int pos,int num){ select(root,pos); select(root->right,num+1); NODE *t=root->right->left; t->rev=!t->rev; splay(root,t); } int Get_Sum(int pos,int num){ select(root,pos); select(root->right,num+1); return root->right->left->sum; } void Max_Sum(){printf("%d\n",root->maxsum);} }tree; int main(){ scanf("%d%d",&n,&m); tree.init(); for(int i=1;i<=n;i++) scanf("%d",&tmp[i]); tree.insert(1,n); while(m--){ scanf("%s",s); switch(s[0]){ case 'I': scanf("%d%d",&pos,&num); if(!num) break; for(int i=1;i<=num;i++) scanf("%d",&tmp[i]); tree.insert(pos+1,num); break; case 'D': scanf("%d%d",&pos,&num); tree.Delete(pos,num); break; case 'R': scanf("%d%d",&pos,&num); tree.Reverse(pos,num); break; case 'G': scanf("%d%d",&pos,&num); printf("%d\n",tree.Get_Sum(pos,num)); break; case 'M': if(s[2]=='K'){ scanf("%d%d%d",&pos,&num,&x); tree.Make_Same(pos,num,x); break; }else tree.Max_Sum(); } } return 0; }