【平衡树】【NOI 2005】维护数列
2015-07-27 19:43
423 查看
339. [NOI2005] 维护数列
[code]★★★★☆ 输入文件:seq2005.in 输出文件:seq2005.out 简单对比 时间限制:3 s 内存限制:256 MB
【问题描述】
请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏 中的下划线‘ _ ’表示实际输入文件中的空格)
【输入格式】
输入文件的第 1 行包含两个数 N 和 M,N 表示初始时数列中数的个数,M表示要进行的操作数目。
第 2 行包含 N 个数字,描述初始时的数列。
以下 M 行,每行一条命令,格式参见问题描述中的表格。
【输出格式】
对于输入数据中的 GET-SUM 和 MAX-SUM 操作,向输出文件依次打印结果,每个答案(数字)占一行。
【输入样例】
[code]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
【输出样例】
[code]-1 10 1 10
【样例说明】
初始时,我们拥有数列 2 -6 3 5 1 -5 -3 6 3
执行操作 GET-SUM 5 4,表示求出数列中从第 5 个数开始连续 4 个数字之和,1+(-5)+(-3)+6 = -1:
[code]2 -6 3 5 1 -5 -3 6 3
执行操作 MAX-SUM,表示要求求出当前数列中最大的一段和,应为 3+5+1+(-5)+(-3)+6+3 = 10:
[code]2 -6 3 5 1 -5 -3 6 3
执行操作 INSERT 8 3 -5 7 2,即在数列中第 8 个数字后插入-5 7 2,
[code]2 -6 3 5 1 -5 -3 6 -5 7 2 3
执行操作 DELETE 12 1,表示删除第 12 个数字,即最后一个:
[code]2 -6 3 5 1 -5 -3 6 -5 7 2
执行操作 MAKE-SAME 3 3 2,表示从第 3 个数开始的 3 个数字,统一修改为 2:
[code]2 -6 3 5 1 -5 -3 6 -5 7 2
改为
[code]2 -6 2 2 2 -5 -3 6 -5 7 2
执行操作 REVERSE 3 6,表示取出数列中从第 3 个数开始的连续 6 个数:
[code]2 -6 2 2 2 -5 -3 6 -5 7 2
如上所示的灰色部分 2 2 2 -5 -3 6,翻转后得到 6 -3 -5 2 2 2,并放回原来位置:
[code]2 -6 6 -3 -5 2 2 2 -5 7 2
最后执行 GET-SUM 5 4 和 MAX-SUM,不难得到答案 1 和 10。
[code]2 -6 6 -3 -5 2 2 2 -5 7 2
【评分方法】
本题设有部分分,对于每一个测试点:
如果你的程序能在输出文件正确的位置上打印 GET-SUM 操作的答案,你可以得到该测试点 60%的分数;
如果你的程序能在输出文件正确的位置上打印 MAX-SUM 操作的答案,你可以得到该测试点 40%的分数;
以上两条的分数可以叠加,即如果你的程序正确输出所有 GET-SUM 和MAX-SUM 操作的答案,你可以得到该测试点 100%的分数。
请注意:如果你的程序只能正确处理某一种操作,请确定在输出文件正确的位置上打印结果,即必须为另一种操作留下对应的行,否则我们不保证可以正确评分。
【数据规模和约定】
你可以认为在任何时刻,数列中至少有 1 个数。
输入数据一定是正确的,即指定位置的数在数列中一定存在。
50%的数据中,任何时刻数列中最多含有 30 000 个数;
100%的数据中,任何时刻数列中最多含有 500 000 个数。
100%的数据中,任何时刻数列中任何一个数字均在[-1 000, 1 000]内。
100%的数据中,M ≤20 000,插入的数字总数不超过 4 000 000 个,输入文件大小不超过 20MBytes。
题解:
一道十分综合的区间操作题,某神犇说1小时内写完A掉基本区间问题就可以无忧了。。然而我太弱了,写了4h+。。
每个节点需要保存的信息:
v:点权; p:键值; cnt:重复(也可以不用); sz:子树大小; sum:区间点权和;
ls:从最左边延伸出的最大连续区间和;
rs:从最右边延伸出的最大连续区间和;
maxs:整个区间的最大连续区间和;
rev:反转标记; c:修改标记;
维护:
ls=max(left->ls,left->sum+x->v+max(0,r->ls));
rs=max(right->rs,max(0,left->rs)+x->v+right->sum);
maxs=max(max(0,left->rs)+x->v+max(0,right->ls),max(left->maxs,right->maxs));
翻转交换子树和ls,rs。
具体细节注意一下就好了。
Insert:暴力插每个数。
Delete:删除区间。可以写垃圾回收。不过我懒的写了。。
Make-same:打标记,要一路下放。
Reverse:打标记,同样一路下放。
Get-sum:找到区间,直接查询即可。
Max-sum:直接查根节点maxs即可。
Code:
[code]#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #define inf -0x7fffffff using namespace std; struct node{ node *left,*right; int v,p,sz,cnt,sum,ls,rs,maxs,rev,c; }*root,*null=new node((node){null,null,0,0,0,0,inf,inf,inf,inf,0,inf}); typedef pair<node*,node*> Dnode; void push_up(node *x){ node *l=x->left,*r=x->right; x->sz=x->cnt+l->sz+r->sz; //sum x->sum=x->v; if (l->sz) x->sum+=l->sum; if (r->sz) x->sum+=r->sum; //ls if (l->sz) x->ls=max(l->ls,l->sum+x->v+max(0,r->ls)); else x->ls=max(x->v,x->v+max(0,r->ls)); //rs if (r->sz) x->rs=max(r->rs,max(0,l->rs)+x->v+r->sum); else x->rs=max(x->v,x->v+max(0,l->rs)); //maxs if (l->sz && r->sz) x->maxs=max(0,l->rs)+x->v+max(0,r->ls); else { if (l->sz) x->maxs=max(l->rs,0)+x->v; else if (r->sz) x->maxs=max(0,r->ls)+x->v; else x->maxs=x->v; } if (l->sz) x->maxs=max(x->maxs,l->maxs); if (r->sz) x->maxs=max(x->maxs,r->maxs); x->maxs=max(x->maxs,x->sum); } void push_down(node *x){ node *l=x->left,*r=x->right; if (x->c!=inf){ if (l->sz){ l->c=l->v=x->c; l->sum=l->v*l->sz; l->ls=l->rs=l->maxs=max(l->v,l->sum); } if (r->sz){ r->c=r->v=x->c; r->sum=r->v*r->sz; r->ls=r->rs=r->maxs=max(r->v,r->sum); } x->c=inf; } if (x->rev%2){ if (l->sz){ swap(l->left,l->right); swap(l->ls,l->rs); l->rev++; } if (r->sz){ swap(r->left,r->right); swap(r->ls,r->rs); r->rev++; } x->rev=0; } } node *merge(node *x,node *y){ if (!x->sz) return y; if (!y->sz) return x; push_down(x); push_down(y); if (x->p<y->p){ x->right=merge(x->right,y); push_up(x); return x; } else { y->left=merge(x,y->left); push_up(y); return y; } } Dnode split(node *x,int k){ if (!x->sz) return Dnode(null,null); Dnode y; push_down(x); if (k<=x->left->sz){ y=split(x->left,k); x->left=y.second; push_up(x); y.second=x; } else { y=split(x->right,k-x->left->sz-x->cnt); x->right=y.first; push_up(x); y.first=x; } return y; } void Insert(int x,int k){ Dnode y=split(root,x); node *z; for (int i=1; i<=k; i++){ int v; scanf("%d",&v); z=new node; z->v=z->sum=z->ls=z->rs=z->maxs=v; z->left=z->right=null; z->c=inf; z->sz=z->cnt=1; z->p=rand(); z->rev=0; y.first=merge(y.first,z); } root=merge(y.first,y.second); } void Delete(int x,int k){ Dnode y,z; y=split(root,x-1); z=split(y.second,k); root=merge(y.first,z.second); } void Change(int x,int k,int v){ Dnode y,z; y=split(root,x-1); z=split(y.second,k); z.first->c=z.first->v=v; z.first->sum=v*z.first->sz; z.first->ls=z.first->rs=z.first->maxs=max(v,z.first->sum); push_down(z.first); y.second=merge(z.first,z.second); root=merge(y.first,y.second); } void Reverse(int x,int k){ Dnode y,z; y=split(root,x-1); z=split(y.second,k); z.first->rev++; swap(z.first->left,z.first->right); swap(z.first->ls,z.first->rs); push_down(z.first); y.second=merge(z.first,z.second); root=merge(y.first,y.second); } void Q_sum(int x,int k){ Dnode y,z; if (!k){ printf("0\n"); return; } y=split(root,x-1); z=split(y.second,k); push_up(z.first); printf("%d\n",z.first->sum); y.second=merge(z.first,z.second); root=merge(y.first,y.second); } int main(){ int n,m; root=null; char opt[20]; scanf("%d%d",&n,&m); Insert(0,n); while (m--){ scanf("%s",&opt); int x,y,z; switch (opt[0]){ case 'I':scanf("%d%d",&x,&y); Insert(x,y); break; case 'D':scanf("%d%d",&x,&y); Delete(x,y); break; case 'M':{ if (opt[2]=='K') scanf("%d%d%d",&x,&y,&z),Change(x,y,z); else printf("%d\n",root->maxs); break; } case 'R':scanf("%d%d",&x,&y); Reverse(x,y); break; case 'G':scanf("%d%d",&x,&y); Q_sum(x,y); break; } // cout<<root->v<<" "<<root->p<<" "<<root->sz<<" "<<root->sum<<" "<<root->ls<<" "<<root->rs<<" "<<root->maxs<<endl; } return 0; }
相关文章推荐
- linux 查看进程的命令(top)
- 新手站长做网站优化时请避免这些SEO问题
- Tomcat启动脚本
- linux pwd命令详解
- ATMEGA16 IOport相关汇总
- OJ网站
- 内存管理与copy和mutableCopy
- linux /etc/security/limits.conf的相关说明
- linux 目录结构
- DB2日常运维之总结(转)
- windows下强制杀死tomcat进程
- 用于学习的好网站
- Linux命令之大文件切分与合并
- shell常用的 if语句【-x file】
- Linux系统下安装rz/sz命令及使用说明
- Linux的磁盘管理
- shell
- 利用iptables给Docker绑定一个外网IP
- 解决8080端口被占,Tomcat无法正常启动
- centos 安装 mysql