[BZOJ1500][NOI2005]维修数列(splay)

33 篇文章 0 订阅
26 篇文章 0 订阅

题目描述

传送门

题解

splay模板题(笑
这题是我今年1月左右看到的。记得当时只调过了样例然后交上去过了一个点就用了我一天的时间。果然我还是太傻逼。
填上了好大一个坑。
前5个都是splay的基础操作,就是第6个比较麻烦。因为要维护区间最大连续子序列和,可以维护3个值:Max子树最大连续子序列和,maxl子树自左端起最大连续子序列和,maxr子树自右端起最大连续子序列和。注意这里的Max是不允许有空串的,因为这个即为最后的答案,但是maxl和maxr都是允许有空串的,这样的话update的时候比较好搞。

坑点:
①覆盖的值可能为0,所以覆盖的标记刚开始不能为0.
②由于Max是不允许有空串的,而maxl和maxr是允许有空串的,所以刚开始要将Max[0]赋为-inf,否则的话如果某个点没有儿子的话求Max的时候会出错。
③打翻转标记的含义为“对当前点做过但是对当前点的儿子没有做过”,也就是说打了标记的这个点,它的左右两个儿子已经交换了,但是它的儿子的儿子没有交换。这个和平常写splay的时候有区别。原因在于,在更新x的时候x的两个儿子的maxl和maxr对x是有影响的,所以不能在没有pushdown的情况下直接更新。不要忘了翻转的时候maxl和maxr也是要交换的。
④内存池的使用:每一次删除一段区间,实际上是将一棵树丢到内存池里,每次从内存池中弹出一个点时,要将它的两个儿子的子树重新丢到内存池里。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 500005
#define inf 1000000000

int n,m,pos,tot,c,root;
int l,r,q[5000005];
int a[N],b[N],f[N],ch[N][2],size[N],key[N],same[N],rev[N],sum[N],Max[N],maxl[N],maxr[N];
char opt[20];

void clear(int x)
{
    f[x]=ch[x][0]=ch[x][1]=size[x]=key[x]=rev[x]=sum[x]=maxl[x]=maxr[x]=0;
    same[x]=inf;
    Max[x]=-inf;
}
void delp(int x)
{
    q[++r]=x;
}
int newp()
{
    ++l;
    if (ch[q[l]][0]) delp(ch[q[l]][0]);
    if (ch[q[l]][1]) delp(ch[q[l]][1]);
    clear(q[l]);
    return q[l];
}
int get(int x)
{
    return ch[f[x]][1]==x;
}
void update(int x)
{
    size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
    sum[x]=key[x]+sum[ch[x][0]]+sum[ch[x][1]];
    maxl[x]=max(maxl[ch[x][0]],sum[ch[x][0]]+key[x]+maxl[ch[x][1]]);
    maxr[x]=max(maxr[ch[x][1]],sum[ch[x][1]]+key[x]+maxr[ch[x][0]]);
    Max[x]=max(Max[ch[x][0]],Max[ch[x][1]]);
    Max[x]=max(Max[x],maxr[ch[x][0]]+key[x]+maxl[ch[x][1]]);
}
void pushdown(int x)
{
    if (x)
    {
        if (same[x]!=inf)
        {
            if (ch[x][0])
            {
                key[ch[x][0]]=same[x];
                sum[ch[x][0]]=same[x]*size[ch[x][0]];
                if (same[x]>=0)
                    Max[ch[x][0]]=maxl[ch[x][0]]=maxr[ch[x][0]]=sum[ch[x][0]];
                else
                    Max[ch[x][0]]=same[x],maxl[ch[x][0]]=maxr[ch[x][0]]=0;
                same[ch[x][0]]=same[x];
            }
            if (ch[x][1])
            {
                key[ch[x][1]]=same[x];
                sum[ch[x][1]]=same[x]*size[ch[x][1]];
                if (same[x]>=0)
                    Max[ch[x][1]]=maxl[ch[x][1]]=maxr[ch[x][1]]=sum[ch[x][1]];
                else
                    Max[ch[x][1]]=same[x],maxl[ch[x][1]]=maxr[ch[x][1]]=0;
                same[ch[x][1]]=same[x];
            }
            same[x]=inf;
        }
        if (rev[x])
        {
            if (ch[x][0])
            {
                int now=ch[x][0];
                rev[now]^=1;
                swap(ch[now][0],ch[now][1]);
                swap(maxl[now],maxr[now]);
            }
            if (ch[x][1])
            {
                int now=ch[x][1];
                rev[now]^=1;
                swap(ch[now][0],ch[now][1]);
                swap(maxl[now],maxr[now]);
            }
            rev[x]=0;
        }
    }
}
int build(int l,int r,int fa,int *a)
{
    if (l>r) return 0;
    int mid=(l+r)>>1,now=newp();
    f[now]=fa;key[now]=a[mid];Max[now]=a[mid];
    int lch=build(l,mid-1,now,a);
    int rch=build(mid+1,r,now,a);
    ch[now][0]=lch,ch[now][1]=rch;
    update(now);
    return now;
}
void rotate(int x)
{
    pushdown(f[x]);
    pushdown(x);
    int old=f[x],oldf=f[old],wh=get(x);
    ch[old][wh]=ch[x][wh^1];
    if (ch[old][wh]) f[ch[old][wh]]=old;
    ch[x][wh^1]=old;
    f[old]=x;
    if (oldf) ch[oldf][ch[oldf][1]==old]=x;
    f[x]=oldf;
    update(old);
    update(x);
}
void splay(int x,int tar)
{
    for (int fa;(fa=f[x])!=tar;rotate(x))
        if (f[fa]!=tar)
            rotate( (get(x)==get(fa))?fa:x );
    if (!tar) root=x;
}
int find(int x)
{
    int now=root;
    while (1)
    {
        pushdown(now);
        if (x<=size[ch[now][0]]) now=ch[now][0];
        else
        {
            x-=size[ch[now][0]];
            if (x==1) return now;
            --x;
            now=ch[now][1];
        }
    }
}
int main()
{
    freopen("input.in","r",stdin);
    freopen("my.out","w",stdout);
    scanf("%d%d",&n,&m);
    a[1]=-inf;a[n+2]=inf;Max[0]=-inf;
    for (int i=1;i<=n;++i) scanf("%d",&a[i+1]);
    for (int i=1;i<=500000;++i) delp(i);
    root=build(1,n+2,0,a);
    for (int i=1;i<=m;++i)
    {
        scanf("%s",opt);
        switch(opt[0])
        {
            case 'I':
                {
                    scanf("%d%d",&pos,&tot);
                    for (int i=1;i<=tot;++i) scanf("%d",&b[i]);
                    int now=build(1,tot,0,b);
                    int aa=find(pos+1);
                    int bb=find(pos+2);
                    splay(aa,0);
                    splay(bb,aa);
                    ch[ch[root][1]][0]=now;
                    f[now]=ch[root][1];
                    update(ch[root][1]);
                    update(root);
                    break;
                }
            case 'D':
                {
                    scanf("%d%d",&pos,&tot);
                    int aa=find(pos);
                    int bb=find(pos+tot+1);
                    splay(aa,0);
                    splay(bb,aa);
                    delp(ch[ch[root][1]][0]);
                    ch[ch[root][1]][0]=0;
                    update(ch[root][1]);
                    update(root);
                    break;
                }
            case 'R':
                {
                    scanf("%d%d",&pos,&tot);
                    if (tot==1) continue;
                    int aa=find(pos);
                    int bb=find(pos+tot+1);
                    splay(aa,0);
                    splay(bb,aa);
                    int now=ch[ch[root][1]][0];
                    swap(ch[now][0],ch[now][1]);
                    swap(maxl[now],maxr[now]);
                    rev[now]^=1;
                    break;
                }
            case 'G':
                {
                    scanf("%d%d",&pos,&tot);
                    int aa=find(pos);
                    int bb=find(pos+tot+1);
                    splay(aa,0);
                    splay(bb,aa);
                    int ans=sum[ch[ch[root][1]][0]];
                    printf("%d\n",ans);
                    break;
                }
            case 'M':
                {
                    if (opt[2]=='K')
                    {
                        scanf("%d%d%d",&pos,&tot,&c);
                        int aa=find(pos);
                        int bb=find(pos+tot+1);
                        splay(aa,0);
                        splay(bb,aa);
                        int now=ch[ch[root][1]][0];
                        key[now]=c;
                        sum[now]=c*size[now];
                        if (c>=0)
                            Max[now]=maxl[now]=maxr[now]=sum[now];
                        else
                            Max[now]=c,maxl[now]=maxr[now]=0;
                        same[now]=c;
                        update(ch[root][1]);
                        update(root);
                    }
                    else
                    {
                        int aa=find(1);
                        int bb=find(size[root]);
                        splay(aa,0);
                        splay(bb,aa);
                        int ans=Max[ch[ch[root][1]][0]];
                        printf("%d\n",ans);
                    }
                    break;
                }
        }
    }
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值