BZOJ1500: [NOI2005]维修数列(洛谷P2042)
来源:互联网 发布:五毛特效是什么软件 编辑:程序博客网 时间:2024/05/29 06:41
Splay
BZOJ题目传送门
洛谷题目传送门
题外话
毒瘤题啊!搞了我一个星期。。。重码第三遍才过。。。
建议当你内心非常平静/特别想发泄的时候来码这道题。
正题
常规Splay维护区间,下面给出实现各种操作的思想:
Insert:套路地把L-1和R+1(其实就是L+1)分别转到根与根的右子树,此时区间
Delete:同Insert伸展。然后直接把R的左子树清零就好了。
Make-Same:和线段树一样打个Lazy-Tag,不断下传即可。
Reverse:同样也是打个Tag,然后把左右儿子交换即可。注意实时更新信息。
Get-Sum:新增一个变量sum记录子树权值和,直接输出即可。
Max-Sum:这个比较烦。新增变量l表示从区间左端点开始的前缀最大值,r表示从区间右端点开始的后缀最大值,mx表示这个区间中的最大子序列。那么在合并
具体还是看代码吧。细节巨多。
这道题空间卡的比较紧,因此我用了内存池。
代码:
#include<cctype>#include<cstdio>#include<algorithm>#include<cstring>#include<queue>using namespace std;const int inf=0x3f3f3f3f;const int N=1e6+5;struct node{ int fa,sz,to[2],w,l,r,mx,t1,t2,sum;}t[N];int n,m,rt,nd,tp,a[N],id[N],stack[N];int _read(){//读优 int num=0,f=1; char ch=getchar(); while (!isdigit(ch)){if (ch=='-')f=-1;ch=getchar();} while (isdigit(ch)) num=num*10+ch-48,ch=getchar(); return num*f;}void pshp(int x){//重新计算(pushup) int l=t[x].to[0],r=t[x].to[1]; t[x].sum=t[l].sum+t[r].sum+t[x].w; t[x].sz=t[l].sz+t[r].sz+1; t[x].mx=max(t[l].mx,max(t[r].mx,t[l].r+t[x].w+t[r].l)); //当前区间的最大子序列=max(左区间的,右区间的,左区间前缀+右区间后缀+当前值) t[x].l=max(t[l].l,t[l].sum+t[x].w+t[r].l); //当前区间的最大前缀=max(左区间的,左区间之和+当前值+右区间的) t[x].r=max(t[r].r,t[r].sum+t[x].w+t[l].r); //当前区间的最大后缀=max(右区间的,右区间之和+当前值+左区间的)}void pshd(int x){//下传标记(pushdown) int l=t[x].to[0],r=t[x].to[1]; if (t[x].t1){ t[x].t2=t[x].t1=0;//如果值相同那么翻不翻都一样 if (l) t[l].t1=1,t[l].sum=(t[l].w=t[x].w)*t[l].sz; if (r) t[r].t1=1,t[r].sum=(t[r].w=t[x].w)*t[r].sz; if (t[x].w>=0){//更新前后缀 if (l)t[l].l=t[l].r=t[l].mx=t[l].sum; if (r)t[r].l=t[r].r=t[r].mx=t[r].sum; } else{//小于零=不取 if (l)t[l].l=t[l].r=0,t[l].mx=t[x].w; if (r)t[r].l=t[r].r=0,t[r].mx=t[x].w; } } if (t[x].t2){ t[x].t2=0,t[l].t2^=1,t[r].t2^=1; swap(t[l].l,t[l].r),swap(t[r].l,t[r].r);//全都要交换 swap(t[l].to[0],t[l].to[1]),swap(t[r].to[0],t[r].to[1]); }}void rtt(int x,int &w){//旋转(rotate) int y=t[x].fa,z=t[y].fa,l=(t[y].to[1]==x),r=l^1; if (y==w) w=x; else t[z].to[t[z].to[1]==y]=x; t[t[x].to[r]].fa=y,t[y].fa=x,t[x].fa=z; t[y].to[l]=t[x].to[r],t[x].to[r]=y; pshp(y),pshp(x);//记得更新}void splay(int x,int &w){//伸展 while (x!=w){ int y=t[x].fa,z=t[y].fa; if (y!=w) if (t[z].to[0]==y^t[y].to[0]==x) rtt(x,w); else rtt(y,w); rtt(x,w); }}int srch(int x,int w){//寻找节点位置(search) pshd(x); int l=t[x].to[0]; //因为所有修改操作都要用到srch,所以就直接在这里下传标记了 if (t[l].sz+1==w) return x; if (t[l].sz>=w) return srch(l,w); return srch(t[x].to[1],w-t[l].sz-1);}void rcycl(int x){//回收内存(recycle) //没用过的话多看几遍也就懂了,挺好理解的 int &l=t[x].to[0],&r=t[x].to[1]; if (l) rcycl(l); if (r) rcycl(r); stack[++tp]=x,t[x].fa=l=r=t[x].t1=t[x].t2=0;}int sprt(int x,int w){//把[l,r]分离出来(seperate) int p=srch(rt,x),q=srch(rt,x+w+1);//分别把l-1和r+1伸展到对应位置 splay(p,rt),splay(q,t[p].to[1]); return t[q].to[0];//返回r+1的左子树}void build(int l,int r,int fa){//建树 if (l>r) return; int mid=(l+r)>>1,x=id[mid]; if (l==r){ t[x].mx=t[x].sum=a[l],t[x].t1=t[x].t2=0; t[x].l=t[x].r=max(a[l],0),t[x].sz=1; } build(l,mid-1,mid),build(mid+1,r,mid); t[x].w=a[mid],t[x].fa=id[fa]; pshp(x),t[id[fa]].to[mid>=fa]=x;}void nsrt(int x,int w){//插入(insert) for (int i=1;i<=w;i++) a[i]=_read(); for (int i=1;i<=w;i++) if (tp) id[i]=stack[tp--]; else id[i]=++nd; build(1,w,0);//先把插入的节点建树 int z=id[(1+w)>>1],p=srch(rt,x+1),q=srch(rt,x+2); splay(p,rt),splay(q,t[p].to[1]); t[z].fa=q,t[q].to[0]=z,pshp(q),pshp(p);//直接接上去}void dlt(int x,int w){//删除(delete) int p=sprt(x,w),q=t[p].fa; rcycl(p),t[q].to[0]=0; pshp(q),pshp(t[q].fa);}void mdfy(int x,int w,int c){//修改(modify) int p=sprt(x,w),q=t[p].fa; t[p].w=c,t[p].t1=1,t[p].sum=t[p].sz*c;//打标记,重新计算 if (c>=0) t[p].l=t[p].r=t[p].mx=t[p].sum; else t[p].l=t[p].r=0,t[p].mx=c; pshp(q),pshp(t[q].fa);}void rvrs(int x,int w){//翻转(reverse) int p=sprt(x,w),q=t[p].fa; if (!t[p].t1){//如果已经修改过那么转与不转就没区别了 t[p].t2^=1,swap(t[p].to[0],t[p].to[1]); swap(t[p].l,t[p].r),pshp(q),pshp(t[q].fa); }}int main(){ n=_read(),m=_read(),t[0].mx=a[1]=a[n+2]=-inf; for(int i=1;i<=n;i++) a[i+1]=_read(); for(int i=1;i<=n+2;i++) id[i]=i;//注意要建虚拟节点 build(1,n+2,0),rt=(n+3)>>1,nd=n+2; int x,w,c; char s[10]; while (m--){ scanf("%s",s); if (s[0]!='M'||s[2]!='X') x=_read(),w=_read(); switch (s[0]){ case 'I': nsrt(x,w); break; case 'D': dlt(x,w); break; case 'M': if (s[2]=='X') printf("%d\n",t[rt].mx); else c=_read(),mdfy(x,w,c); break; case 'R': rvrs(x,w); break; case 'G': printf("%d\n",t[sprt(x,w)].sum); break; } } return 0;}
阅读全文
1 0
- BZOJ1500: [NOI2005]维修数列(洛谷P2042)
- [bzoj1500][NOI2005]维修数列
- bzoj1500: [NOI2005]维修数列
- BZOJ1500 [NOI2005]维修数列
- bzoj1500: [NOI2005]维修数列
- [BZOJ1500][NOI2005]维修数列
- [BZOJ1500][NOI2005]维修数列
- bzoj1500【NOI2005】维修数列
- bzoj1500: [NOI2005]维修数列
- 【bzoj1500】[NOI2005]维修数列
- 【bzoj1500】【NOI2005】维修数列
- bzoj1500: [NOI2005]维修数列
- bzoj1500: [NOI2005]维修数列
- BZOJ1500: [NOI2005]维修数列
- bzoj1500 [NOI2005]维修数列
- 【bzoj1500】[NOI2005]维修数列
- 【bzoj1500】[NOI2005]维修数列
- [BZOJ1500][NOI2005]维修数列
- c#把字符串分割为多个字符串
- WebSocket安卓客户端实现详解(三)–服务端主动通知
- Sublime Text 3 绝对神器
- uva 11991 Easy Problem from Rujia Liu?
- Wait/Notify通知机制解析
- BZOJ1500: [NOI2005]维修数列(洛谷P2042)
- [转载]github连接速度
- 二分查找,递归与非递归
- 《python机器学习及实践-从零开始通往kaggle竞赛之路(代码Python 3.6 版)》chapter2.1.1.2
- ZOJ 3988 Prime Set(二分图)
- 计算1/1-1/2+1/3-1/4+1/5 …… + 1/99
- 【JavaWeb】Mysql source 恢复数据出错解决
- docker开启rabbitmq
- 2. R语言中各种数据类型常见运算的函数