bzoj1500 [NOI2005]维修数列解题报告

来源:互联网 发布:如何申请公司网站域名 编辑:程序博客网 时间:2024/05/02 03:11

Bzoj 1500: [NOI2005]维修数列 解题报告

题目原文

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 10429  Solved: 3218
[Submit][Status][Discuss]

Description

Input

输入的第1 行包含两个数N 和M(M ≤20 000),N 表示初始时数列中数的个数,M表示要进行的操作数目。
第2行包含N个数字,描述初始时的数列。
以下M行,每行一条命令,格式参见问题描述中的表格。
任何时刻数列中最多含有500 000个数,数列中任何一个数字均在[-1 000, 1 000]内。
插入的数字总数不超过4 000 000个,输入文件大小不超过20MBytes。

Output

对于输入数据中的GET-SUM和MAX-SUM操作,向输出文件依次打印结果,每个答案(数字)占一行。

Sample Input

9 8
2 -6 3 5 1 -5 -3 6 3
GET-SUM 5 4
MAX-SUM
INSERT 8 3 -5 7 2
DELETE 12 1
MAKE-SAME 3 3 2
REVERSE 3 6
GET-SUM 5 4
MAX-SUM

Sample Output

-1
10
1
10

HINT

分析

      开门见山,这是一道平衡树题目,我只会用splay,据说这道题还能用treap解决(Orz),此处只介绍splay的做法,splay的基础知识在此不再展开,只介绍针对这道题的操作。
      k:节点在区间中的序号(第几个数),v:结点的权值,maxsum:以此节点为根的子树所对应区间的最大子序列和,lsum:包含区间左端点的最大序列和,rsum:包含区间右端点的最大序列和,totsum:此区间的和(所有数加起来),dk:对k值打的标记,dv:对v值打的标记,表示这个区间的所有值都修改为dv,reverse布尔型变量,表示这个区间是否被翻转,size:以当前节点为根的子树的大小(节点数目)。
      首先,要建一个MIN结点和一个MAX结点,分别代表区间的两端,这样在操作中可以避免一些麻烦,但注意这两个点的权值都为0,否则会影响区间和的查询。
      Insert:只需把pos点splay到根,pos+1splay到根的右儿子,因为pos+1是pos的后继,因此pos+1一定没有左儿子,所以先给pos+1的dk打上增加tot的标记,再把标记下放,最后再把新建成的树挂在其左儿子的位置上。此处插入数据有个问题,已知你插入的数据的k一定是递增的,所以传统的插入会导致该数据连成一条链,因此我们采用分治的方法,取区间的mid作为根节点,然后递归处理左右子树,这样就能够建立起一棵深度为logtot的完全平衡的二叉查找树。
      Delete:只需把pos-1旋转到根,pos+tot旋转到根的右子节点,删除pos+tot的左儿子,再将pos+tot打上-tot的标记。
      Make-same:把pos-1 Splay到根,把pos+tot Splay到根节点右边,将pos+tot的左儿子的dv修改成c。
      Reverse:把pos-1转到根,pos+tot转到根节点右边,将pos+tot的reverse标记取反。
      Get-sum:把pos-1转到根,pos+tot转到根的右边,输出pos+tot左儿子的totsum。
      Max-sum:把MIN转到根,MAX转到MIN的右边,输出MAX左儿子的maxsum值。
      以上是很好想的,这道题的重点在于怎样将标记下放以及怎样计算lsum、rsum、totsum、maxsum以及怎样翻转。
      不多说,直接给出计算公式:
      lsum:设左右子树分别为l、r,当前节点为x,则x->lsum=max{ x->l->lsum , [x->l->totsum + x->v + max(0,x->r->lsum)] }
      rsum:x->rsum=max{ x->r->rsum , [x->r->totsum + max(0,x->v +x->l->rsum)] }
      maxsum:max{ x->l->maxsum , x->r->maxsum , [ x->v + max(0,x->l->rsum) + max(0,x->r->lsum) ] }
      totsum:x->totsum = x->l->totsum + x->v + x->r->totsum
      size:x->size = x->l->size + x->r->size + 1
      翻转操作:首先修改x->k,x->k = x->k - x->l->size + x->r->size,然后给左右儿子打标记,可以先不考虑左右儿子的翻转,先认为左右儿子区间调换了位置,将区间整体平移,x->l->dk += 1 + x->r->size,x->r->dk-=1+x->l->size,翻转操作结束
      还有一个问题就是,题目中说任何时刻区间中做多有500 000个数,大约是19MB空间,而插入数据最多有4 000 000,大约是152MB空间,后者会爆内存的,像我这样用指针和new操作节点,删除很不方便,你想想,我每次都是删除一棵子树,我只能把边断开,而不能把节点删去,我总不能用O(N)的时间再去一个一个删节点吧,本来nlogn就很擦边,常数还很大,不可能再去用O(N)的时间删节点,由此我冥思苦想,想到一个解决方案:在程序最开始先建立一棵空树,什么信息也没有,但有500 000个节点,每次要取节点就从上面取,每次删除的树直接挂上去,取的时间复杂度和挂的时间复杂度都是O(1)的,这样我就能在不影响时间复杂度的情况下,保证最多同时存在500 000个节点。这个方法不仅适用于此题,所有平衡树的题都可以适用,有人觉得这没什么卵用,因为你完全可以开500 000的数组,也能保证空间,但众所周知指针写起来比数组方便多了,这种方法可以让你在用指针的情况下,还可以控制数据的大小,真是个好东西得意(程序中详见get和ins函数)。
      吐槽一下:这道题的原题其实叫“维护数列”
      代码如下:
//bzoj1500[NOI2005]维修数列 Splay#include<cstdio>#include<cstring>#include<algorithm>#define inf 0x3f3f3f3fusing namespace std;int N, M, cnt, c[500050];struct NODE{int lsum, rsum, totsum, maxsum, size, dk, dv, v, k; bool rev;NODE *l, *r, *f;void init(int kk, int vv){k=kk;v=vv;size=1;dv=inf;totsum=lsum=rsum=maxsum=v; l=r=f=0;dk=rev=0;}}*ROOT, *MIN, *MAX, *P;int read(){int x=0; bool fu=false;char c=getchar();while((c<'0'||c>'9')&&c!='-')c=getchar();if(c=='-')fu=true,c=getchar();while(c>='0' && c<='9')x=x*10+c-'0',c=getchar();return (fu?-x:x);}void swap(int &a, int &b){int t=a; a=b; b=t;}void pushdown(NODE *x){if(x->dv<inf){x->v=x->dv; x->totsum=x->v*x->size;x->lsum=x->rsum=x->maxsum=max(x->v,x->totsum);if(x->l)x->l->dv=x->dv; if(x->r)x->r->dv=x->dv;x->dv=inf;}if(x->dk){x->k+=x->dk;if(x->l)x->l->dk+=x->dk; if(x->r)x->r->dk+=x->dk;x->dk=0;}if(x->rev){int sl=0, sr=0;if(x->l)sl=x->l->size,x->l->rev=!(x->l->rev);if(x->r)sr=x->r->size,x->r->rev=!(x->r->rev);x->k=x->k-sl+sr;if(x->l)x->l->dk+=sr+1;if(x->r)x->r->dk+=-sl-1;swap(x->l,x->r);swap(x->lsum,x->rsum);x->rev=false;}}void update(NODE *x){pushdown(x);if(x->l)pushdown(x->l);if(x->r)pushdown(x->r);int t;x->size=1;if(x->l)x->size+=x->l->size; if(x->r)x->size+=x->r->size;x->totsum=x->v;if(x->l)x->totsum+=x->l->totsum; if(x->r)x->totsum+=x->r->totsum;t=x->v;if(x->l)t+=max(0,x->l->rsum);if(x->r)t+=max(0,x->r->lsum);if(x->l)t=max(t,x->l->maxsum);if(x->r)t=max(t,x->r->maxsum);x->maxsum=t;if(x->l){t=max(x->l->lsum,x->l->totsum+x->v);if(x->r)t=max(t,x->l->totsum + x->v + max(0,x->r->lsum) );}else if(x->r)t=x->v+max(0,x->r->lsum);x->lsum=t;if(x->r){t=max(x->r->rsum,x->v+x->r->totsum);if(x->l)t=max(t,x->r->totsum + x->v + max(0,x->l->rsum));}else if(x->l)t=x->v+max(0,x->l->rsum);x->rsum=t;}void zig(NODE *x){NODE *y=x->f;pushdown(y),pushdown(x);if(y->f->l==y)y->f->l=x; else y->f->r=x;x->f=y->f;y->l=x->r; if(x->r)x->r->f=y;x->r=y;y->f=x;update(y);update(x);}void zag(NODE *x){NODE *y=x->f;pushdown(y),pushdown(x);if(y->f->l==y)y->f->l=x; else y->f->r=x;x->f=y->f;y->r=x->l; if(x->l)x->l->f=y;x->l=y;y->f=x;update(y);update(x);}void splay(NODE *x, NODE *F){while(x->f!=F){NODE *y=x->f;if(y->f==F)if(y->l==x)zig(x);else zag(x);elseif(y->f->l==y)if(y->l==x)zig(y),zig(x);else zag(x),zig(x);elseif(y->r==x)zag(y),zag(x);else zig(x),zag(x);}}NODE* find(NODE *x, int k){NODE *t;pushdown(x);if(x->k==k)t=x;if(x->k>k)t=find(x->l,k);if(x->k<k)t=find(x->r,k);update(x);return t;}NODE* get(int k, int v){NODE *t;t=P;P=P->f;if(P->l==t)P->l=0;else P->r=0;while(P->l||P->r)if(P->l)P=P->l;else P=P->r;t->init(k,v);return t;}void ins(NODE *t){P->l=t;t->f=P;while(P->l||P->r)if(P->l)P=P->l;else P=P->r;}NODE* build(int l, int r){int mid=(l+r)>>1; NODE *p=get(mid,c[mid]);if(l==r)return p;if(l!=mid)p->l=build(l,mid-1),p->l->f=p;p->r=build(mid+1,r),p->r->f=p;update(p);return p;}int main(){char s[100], ch;int x, i, pos, tot, j;NODE *p, *t;P=new NODE;for(i=2;i<=500005;i++){t=new NODE;t->init(0,0);t->f=P;P->r=t;P=t;}N=read();M=read();{cnt=0;MIN=get(0,0);MAX=get(1,0);ROOT=get(123,0);ROOT->l=MIN;MIN->f=ROOT;MIN->r=MAX;MAX->f=MIN;for(i=1;i<=N;i++)c[i]=read();MAX->k+=N;MAX->l=build(1,N);MAX->l->f=MAX;update(MAX);update(MIN);for(i=1;i<=M;i++){scanf("%s",s);switch(s[0]){case 'I':pos=read();tot=read();for(j=pos+1;j<=pos+tot;j++)c[j]=read();p=find(ROOT->l,pos);t=find(ROOT->l,pos+1);splay(p,ROOT);splay(t,p);t->dk+=tot;pushdown(t);t->l=build(pos+1,pos+tot);t->l->f=t;update(t);update(p);break;case 'D':pos=read();tot=read();if(!tot)break;p=find(ROOT->l,pos-1);t=find(ROOT->l,pos+tot);splay(p,ROOT);splay(t,p);ins(t->l);t->l=0;t->dk-=tot;pushdown(t);update(t);update(p);break;case 'R':pos=read();tot=read();if(!tot)break;p=find(ROOT->l,pos-1);t=find(ROOT->l,pos+tot);splay(p,ROOT);splay(t,p);update(p);t->l->rev=!(t->l->rev);break;case 'G':pos=read();tot=read();if(!tot){printf("0\n");break;}p=find(ROOT->l,pos-1);t=find(ROOT->l,pos+tot);splay(p,ROOT);splay(t,p);printf("%d\n",t->l->totsum);break;case 'M':if(s[2]=='K'){pos=read();tot=read();c[0]=read();if(!tot)break;p=find(ROOT->l,pos-1);t=find(ROOT->l,pos+tot);splay(p,ROOT);splay(t,p);t->l->dv=c[0];pushdown(t->l);update(t);update(p);break;}splay(MIN,ROOT);splay(MAX,MIN);pushdown(MAX->l);printf("%d\n",MAX->l->maxsum);}}}return 0;}


0 0
原创粉丝点击