BZOJ 1500 [NOI2005]维修数列 (无旋treap良心题解)

题目传送门

上面链接沉了幸亏洛谷还有这道题


无旋treap解题的原理:

1.无旋treap可以在维护平衡的同时保持中序遍历不变(treap也可以,应该都可以),因此我们用下标代表插入二叉树的值,那么我们可以按照中序遍历获得原来的数组。

2.无旋treap的分裂操作可以分裂出包含前k个节点的树和剩下元素的树,就相当于可以把数组切开。

你可以切取想要的区间进行操作,操作完再merge回去即可。

 

上面讲的很关键,先理解这两点再看下面就简单了。

 

多BB一句(不一定对,只是个人理解):普通带旋转的treap虽然中序遍历是对的,但是不能像无旋treap这样【抠出一段区间,然后对于这段区间的根节点搞个下放标记就完成操作。】  因此,普通treap不支持区间操作,并且可持久化也很麻烦。而无旋treap可持久化很方便。


这样脑中至少就有了抽象的思路:

插入:从pos切开,将我们要插入的tot个数列先搞成一棵树,然后三棵树按顺序合并。

删除:pos,pos+tot处共切两刀,中间那棵树不要了,旁边两棵树合并

修改:pos,pos+tot处共切两刀,中间那颗树上的点全是要修改的,你有感觉:这里要下放标记,不然整棵改会超时

翻转:同修改

求和:显然上面如果维护对了那么这里鸡毛问题都没有,可以直接维护的。

求和最大的子列:这个玩意儿做线段树的时候碰到过(有兴趣做做 )

维护三个东西,区间最大前缀和l,区间最大后缀和r,以及我们所需要的区间和最大子列mcs。

我们发现如果我们可以在分裂和合并的时候正确维护信息,就相当于在做区间合并的操作,因此线段树行,平衡树也行!

具体是这样的:

l = max(左儿子l, 左儿子区间和+当前节点的值,左儿子区间和+当前节点的值+右儿子l)

r = max(右儿子r,右儿子区间和+当前节点的值,右儿子区间和+当前节点的值+左儿子r)

mcs = max(左儿子mcs,右儿子mcs,max(左儿子r,0) + 当前节点的值 + max(右儿子l,0))


接下去就是意识流讲解:

一、merge与split

我们先假设push_down(rt)可以正确下放标记并使当前节点的所有需要的信息正确。

再假设update(rt)可以正确更新当前节点的所有信息

毕竟不可能一下子想通整体,只能先假设局部可行了。

下面是结构体内全部的数组

    int ch[maxn][2];///左右儿子
    int pri[maxn];///用于维护堆性质的优先级
    int cnt[maxn];///当前节点包含子节点数量
    int val[maxn];///当前节点的值
    int sum[maxn];///当前节点所在树的数的总和
    int l[maxn],r[maxn],mcs[maxn];///前缀和,后缀和,区间最大和
    int rev[maxn],upd[maxn];///两个lazy标记,翻转标记和覆盖标记
    int sta[maxn];///用于建树的栈,,,这个可以等会儿再说
    int root;
    int nextpos[maxn],st,ed;///储存下一个可以用的节点编号,这个是因为空间开不起,最后再看好了,很简单的...

那么有了前面的假设我们的split和merge函数应该这么写:

    int merge(int x,int y){///合并x和y
        if (!x || !y) return x+y;
        push_down(x),push_down(y);
        if (pri[x] < pri[y]){///维护大根堆
            ch[y][0] = merge(x,ch[y][0]);
            update(y);
            return y;
        }
        else {
            ch[x][1] = merge(ch[x][1],y);
            update(x);
            return x;
        }
    }

    int ccnt;
    void split(int rt,int k,int& x,int& y){///按
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值