BZOJ1500: [NOI2005]维修数列(洛谷P2042)

来源:互联网 发布:五毛特效是什么软件 编辑:程序博客网 时间:2024/05/29 06:41

Splay

BZOJ题目传送门
洛谷题目传送门

题外话

毒瘤题啊!搞了我一个星期。。。重码第三遍才过。。。
建议当你内心非常平静/特别想发泄的时候来码这道题。

正题

常规Splay维护区间,下面给出实现各种操作的思想:

Insert:套路地把L-1和R+1(其实就是L+1)分别转到根与根的右子树,此时区间[L,R]就在R的左子树上。把要插入的值先建成一颗平衡树,再把根节点接到R+1的左子树上,就合并完成了。

Delete:同Insert伸展。然后直接把R的左子树清零就好了。

Make-Same:和线段树一样打个Lazy-Tag,不断下传即可。

Reverse:同样也是打个Tag,然后把左右儿子交换即可。注意实时更新信息。

Get-Sum:新增一个变量sum记录子树权值和,直接输出即可。

Max-Sum:这个比较烦。新增变量l表示从区间左端点开始的前缀最大值,r表示从区间右端点开始的后缀最大值,mx表示这个区间中的最大子序列。那么在合并[l,mid][mid+1,r]时,只需考虑取不取mid的值即可。

具体还是看代码吧。细节巨多。

这道题空间卡的比较紧,因此我用了内存池。

代码:

#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;}