【平衡树维护序列】BZOJ1500(NOI2005)[维修数列]题解

题目概述

给一个序列,然后给出若干个操作,如下:
这里写图片描述

解题报告

最近写了个Splay区间修改,然而模板写的太弱了,听一些大神说BZOJ1500是Splay必A模板题,我立刻就慌了,A掉后来写(水)了个贴。

前面都是废话,正式开始讲题。这道题明显就是变动的序列题目,用Splay再适合不过了,而且还是道非常不错的模板题。前5个操作都是常规的Splay区间操作,就第6个操作比较奇怪(大神可无视),为了得到最大子列,每个节点需要记录这么几个信息:
pre:最大前缀(不可不选)
suf:最大后缀(不可不选)
MAX:最大子列(不可不选)
而最核心的问题就是Pushdown时候的修改,来看两种情况:
1.翻转
翻转的时候,除了交换两个儿子,还要交换pre和suf,MAX不变(因为序列只是正反改变了,相对顺序没有改变)。
2.修改成一样的值
这个比较容易,如果修改的值same>0,那么肯定全选比较好,即:
pre=suf=MAX=same*si。
否则如果same<=0,莫不如选一个(不能不选),即:
pre=suf=MAX=same。
Pushup的时候只需要细心讨论所有情况即可,见代码。

还有就是这道题要循环往复使用节点,所以可以用内存池来解决(或者直接new和delete)。

示例程序

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=500000,maxl=9,maxa=1000,INF=1e9;

int n,te,a[maxn+5];
char s[maxl+5];
//==============================================================
struct Node
{
    Node *son[2];
    int si,num,sum,suf,pre,MAX,tag_same;
    //num表示该节点的值,tag_same是修改的lazy-tag
    bool tag_flip; //翻转的lazy-tag
    int cmp(int &k)
    {
        if (k<son[0]->si+1) return 0;if (k==son[0]->si+1) return -1;
        k-=son[0]->si+1;return 1;
    }
    void Pushup()
    {
        si=son[0]->si+son[1]->si+1;
        sum=son[0]->sum+son[1]->sum+num;
        pre=max(son[0]->pre,max(son[0]->sum+num,son[0]->sum+num+son[1]->pre));
        //son[0]->pre包含了son[0]->sum的情况
        suf=max(son[1]->suf,max(son[1]->sum+num,son[1]->sum+num+son[0]->suf));
        //son[1]->suf包含了son[1]->sum的情况
        MAX=max(son[0]->MAX,son[1]->MAX);
        MAX=max(MAX,max(max(num,son[0]->suf+num+son[1]->pre),max(son[0]->suf+num,num+son[1]->pre)));
        //讨论所有情况,看代码不难看懂,就不解释了:P
    }
};
typedef Node* P_node;
Node tem[maxn+5];
P_node null=tem,ro=null;
int top;P_node stk[maxn+5]; //开个栈表示内存池
P_node newNode(int k) //新建节点
{
    stk[top]->son[0]=stk[top]->son[1]=null;
    stk[top]->tag_flip=0;stk[top]->tag_same=-INF;
    stk[top]->suf=stk[top]->pre=stk[top]->MAX=k;
    stk[top]->si=1;stk[top]->sum=stk[top]->num=k;
    return stk[top--]; //用掉栈顶
}
void delSplay(P_node id) //删除一棵Splay
{
    if (id==null) return;stk[++top]=id; //加回栈顶
    delSplay(id->son[0]);delSplay(id->son[1]);
}
P_node Build(int L,int R,int *a)
{
    if (L>R) return null;
    int mid=L+(R-L>>1);
    P_node now=newNode(a[mid]);
    now->son[0]=Build(L,mid-1,a);now->son[1]=Build(mid+1,R,a);
    now->Pushup();
    return now;
}
void Rotate(P_node &id,int d)
{
    P_node t=id->son[d^1];id->son[d^1]=t->son[d];t->son[d]=id;
    id->Pushup();t->Pushup();id=t;
}
void do_flip(P_node &id) //将id旋转
{
    swap(id->son[0],id->son[1]);swap(id->pre,id->suf);
    id->tag_flip^=1;
}
void do_same(P_node &id,int num) //将id修改
{
    id->num=num;id->sum=id->si*num;
    if (num>0) id->MAX=id->suf=id->pre=id->si*num; else
    id->MAX=id->suf=id->pre=num;
    //讨论num>0或num<=0
    id->tag_same=num;
}
void Pushdown(P_node &id) //传递懒标记
{
    if (id->tag_flip)
    {
        if (id->son[0]!=null) do_flip(id->son[0]);
        if (id->son[1]!=null) do_flip(id->son[1]);
        id->tag_flip=false;
    }
    if (id->tag_same!=-INF)
    {
        if (id->son[0]!=null) do_same(id->son[0],id->tag_same);
        if (id->son[1]!=null) do_same(id->son[1],id->tag_same);
        id->tag_same=-INF;
    }
}
void LNR(P_node id) //debug使用
{
    if (id==null) return;Pushdown(id);
    LNR(id->son[0]);printf(" %d",id->num);LNR(id->son[1]);
}
void Splay(P_node &id,int k)
{
    Pushdown(id);
    int d1=id->cmp(k);
    if (d1!=-1)
    {
        P_node p=id->son[d1];Pushdown(p);int d2=p->cmp(k);
        if (d2!=-1)
        {
            Splay(p->son[d2],k);
            if (d1==d2) Rotate(id,d1^1),Rotate(id,d1^1); else
            Rotate(id->son[d1],d2^1),Rotate(id,d1^1);
        } else Rotate(id,d1^1);
    }
}
void Change(P_node &ro,int L,int R)
{
    L--;R++;
    Splay(ro,L);ro->cmp(R);
    Splay(ro->son[1],R);
}
void Insert(P_node &ro,int L,P_node now)
{
    Change(ro,L+1,L);
    ro->son[1]->son[0]=now;
    ro->son[1]->Pushup();ro->Pushup();
}
void Delete(P_node &ro,int L,int R)
{
    Change(ro,L,R);
    delSplay(ro->son[1]->son[0]);
    ro->son[1]->son[0]=null;
    ro->son[1]->Pushup();ro->Pushup();
}
void make_same(P_node &ro,int L,int R,int num)
{
    Change(ro,L,R);
    do_same(ro->son[1]->son[0],num);
}
void make_flip(P_node &ro,int L,int R)
{
    Change(ro,L,R);
    do_flip(ro->son[1]->son[0]);
}
//==============================================================
bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}
int readi(int &x) //int读入优化
{
    int tot=0,f=1;char ch=getchar(),lst=ch;
    while ('9'<ch||ch<'0') {if (ch==EOF) return EOF;lst=ch;ch=getchar();}
    if (lst=='-') f=-f;
    while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();
    x=tot*f;
    return Eoln(ch);
}
int reads(char *s) //char[]读入优化
{
    int len=0;char ch=getchar();if (ch==EOF) return EOF;
    while (ch!='I'&&ch!='D'&&ch!='M'&&ch!='R'&&ch!='G') ch=getchar();
    s[++len]=ch;while (!Eoln(s[len])&&s[len]!=' ') s[++len]=getchar();s[len--]=0;
    return 0;
}
void Splay_init() //初始化
{
    null->pre=null->suf=null->MAX=-INF; //null不存在pre,suf,MAX
    top=0;for (int i=1;i<maxn+5;i++) stk[++top]=tem+i;
    //初始化内存池
}
int main()
{
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    Splay_init();readi(n);readi(te);
    a[0]=-maxa-1;for (int i=1;i<=n;i++) readi(a[i]);a[n+1]=-maxa-1;
    ro=Build(0,n+1,a);
    while (te--)
    {
        reads(s);
        if (!strcmp(s+1,"INSERT")) //插入
        {
            int L,num;readi(L);readi(num);
            for (int i=1;i<=num;i++) readi(a[i]);
            Insert(ro,L+1,Build(1,num,a));
        } else
        if (!strcmp(s+1,"DELETE")) //删除
        {
            int L,R;readi(L);readi(R);R=L+R-1;
            Delete(ro,L+1,R+1);
        } else
        if (!strcmp(s+1,"MAKE-SAME")) //修改
        {
            int L,R,num;readi(L);readi(R);readi(num);R=L+R-1;
            make_same(ro,L+1,R+1,num);
        } else
        if (!strcmp(s+1,"REVERSE")) //翻转
        {
            int L,R;readi(L);readi(R);R=L+R-1;
            make_flip(ro,L+1,R+1);
        } else
        if (!strcmp(s+1,"GET-SUM")) //求和
        {
            int L,R;readi(L);readi(R);R=L+R-1;
            Change(ro,L+1,R+1);
            printf("%d\n",ro->son[1]->son[0]->sum);
        } else
        Change(ro,1+1,ro->si-1),printf("%d\n",ro->son[1]->son[0]->MAX); //求最大子列
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值