bzoj 3065 带插入区间K小值 重量平衡treap套主席树

原创 2016年08月29日 20:05:58

题意:维护一个数列,支持插入,修改,查询区间k小值。

我数据结构还是太弱呀QAQ
看了myy的课件和vfk的题解

treap支持重量平衡,也就是他插入删除影响的节点数都是O(log) 的。

先说插入,我们先把点插入到最下面,然后往上旋,假设这个点转到祖先k的位置,那么影响节点为sizek 个,由于这个点的随机权值是sizek 个点中最小的,这个的概率是1sizek 的。期望是O(1),一个点有O(log) 个祖先,因此总期望影响节点数O(log)

对于删除,由于treap形态是由随机权值决定的,因此删除的数在treap上位置是随机的。由于treap中一个点的子树的期望大小是O(log) 的。因此是O(log) 的。

treap插入一个元素旋转的期望次数是O(1) 的。因为他的随机权值必须比上面的随机权值大才会旋转,这个次数期望O(1)

前面是几个保证复杂度的东西。

treap每个点代表一个数列中的点。每个点开一棵权值线段树维护子树中的所有点的权值。

由于旋转次数期望O(1),因此可以每次旋转之后直接更新一下信息,不需要最后重构子树里的线段树。
旋转x时,设y是x的父亲,那么旋转后x的线段树和旋转前y的线段树相同,只需更新一下y的线段树。

一开始想用O(log) 的线段树合并,不过线段树合并不支持内存回收,可能MLE,因此写了一个合并到底的O(log2)

查询时用前缀和相减的方法,可以得到log 个线段树或点。在主席树上二分一下时间复杂度O(log2)

学完splay和fhq treap后觉得普通treap已经完全没有用了,不过treap重量平衡的性质还是非常妙的。

好像常数巨大。。。。附上3.7K代码

#include <bits/stdc++.h>
using namespace std;
#define N 71000
#define M 25000000
#define S 70000
#define which(x) ch[fa[x]][1]==x
#define ls ch[x][0]
#define rs ch[x][1]
struct node
{
    int tp1,tp2,x;
    node(){}
    node(int tp1,int tp2,int x):tp1(tp1),tp2(tp2),x(x){}
}st[N];
int top,n,q,a[N];
char s[11];
struct seg_tree
{
    int ch[M][2],sum[M],cnt;
    queue<int>que;
    int ap()
    {
        if(que.empty())return ++cnt;
        int ret=que.front();que.pop();
        ch[ret][1]=ch[ret][0]=0;sum[ret]=0;
        return ret;
    }
    void insert(int l,int r,int &x,int v,int type)
    {
        if(!x)x=ap();sum[x]+=type;
        if(l==r)return;
        int mid=(l+r)>>1;
        if(v<=mid)insert(l,mid,ch[x][0],v,type);
        else insert(mid+1,r,ch[x][1],v,type);
    }
    void insert(int &x,int v,int type)
    {insert(0,S,x,v,type);}
    int merge(int x,int y)
    {
        if(!x&&!y)return 0;
        int ret=ap();
        ch[ret][0]=merge(ch[x][0],ch[y][0]);
        ch[ret][1]=merge(ch[x][1],ch[y][1]);
        sum[ret]=sum[x]+sum[y];
        return ret;
    }
    void del(int x)
    {
        if(!x)return;
        que.push(x);
        del(ls);del(rs);
    }
    int work(int l,int r,int x)
    {
        if(l==r)return l;
        int t=0,mid=(l+r)>>1;
        for(int i=1;i<=top;i++)
        {
            if(st[i].tp2==1)t+=sum[ch[st[i].x][0]]*st[i].tp1;
            else if(st[i].x<=mid&&st[i].x>=l)t+=st[i].tp1;
        }
        if(t>=x)
        {
            for(int i=1;i<=top;i++)
                if(st[i].tp2==1)st[i].x=ch[st[i].x][0];
            return work(l,mid,x);
        }
        else
        {   
            for(int i=1;i<=top;i++)
                if(st[i].tp2==1)st[i].x=ch[st[i].x][1];
            return work(mid+1,r,x-t);
        }
    }
}tr1;
struct treap
{
    int ch[N][2],rt[N],size[N],rnd[N],root,cnt,fa[N],val[N];
    void pushup(int x)
    {
        size[x]=size[ls]+size[rs]+1;
        rt[x]=tr1.merge(rt[ls],rt[rs]);
        tr1.insert(rt[x],val[x],1);
    }
    int build(int l,int r)
    {
        if(l>r)return 0;
        int mid=(l+r)>>1,x=++cnt;
        val[x]=a[mid];rnd[x]=rand()*rand();
        ch[x][0]=build(l,mid-1);
        ch[x][1]=build(mid+1,r);
        pushup(x);
        return x;
    }
    int rotate(int x)
    {
        int y=fa[x],k=which(x);
        ch[y][k]=ch[x][k^1];ch[x][k^1]=y;
        ch[fa[y]][which(y)]=x;

        fa[x]=fa[y];fa[y]=x;
        fa[ch[y][k]]=y;

        tr1.del(rt[x]);rt[x]=rt[y];
        pushup(y);
        size[x]=size[ls]+size[rs]+1;
        return x;
    }
    int insert(int x,int pos,int v)
    {
        if(!x)
        {
            x=++cnt;tr1.insert(rt[x],v,1);
            size[x]=1;rnd[x]=rand()*rand();
            val[x]=v;
            return x;   
        }
        if(pos<=size[ls])
        {
            tr1.insert(rt[x],v,1);size[x]++;
            ls=insert(ls,pos,v);fa[ls]=x;
            if(rnd[ls]>rnd[x])x=rotate(ls);
        }
        else
        {
            tr1.insert(rt[x],v,1);size[x]++;
            rs=insert(rs,pos-size[ls]-1,v);fa[rs]=x;
            if(rnd[rs]>rnd[x])x=rotate(rs);
        }
        return x;
    }
    int change(int x,int pos,int v)
    {
        if(size[ls]+1==pos)
        {
            tr1.insert(rt[x],val[x],-1);
            tr1.insert(rt[x],v,1);
            return x;
        }
        int t;
        if(size[ls]>=pos)t=change(ls,pos,v);
        else t=change(rs,pos-size[ls]-1,v);
        tr1.insert(rt[x],val[t],-1);
        tr1.insert(rt[x],v,1);
        return t;
    }
    void find(int x,int pos,int tp)
    {
        if(!x||!pos)return;
        if(size[x]==pos)
            st[++top]=node(tp,1,rt[x]);
        else if(size[ls]>=pos)
            find(ls,pos,tp);
        else
        {
            st[++top]=node(tp,1,rt[ls]);
            st[++top]=node(tp,0,val[x]);
            find(rs,pos-size[ls]-1,tp);
        }
    }
}tr2;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    tr2.root=tr2.build(1,n);
    scanf("%d",&q);
    for(int x,y,k,val,ans=0;q--;)
    {
        scanf("%s",s);
        if(s[0]=='Q')
        {
            scanf("%d%d%d",&x,&y,&k);
            x^=ans;y^=ans;k^=ans;
            top=0;
            tr2.find(tr2.root,x-1,-1);
            tr2.find(tr2.root,y,1);
            ans=tr1.work(0,S,k);
            printf("%d\n",ans);
        }
        if(s[0]=='M')
        {
            scanf("%d%d",&x,&val);
            x^=ans;val^=ans;
            int t=tr2.change(tr2.root,x,val);
            tr2.val[t]=val;
        }
        if(s[0]=='I')
        {
            scanf("%d%d",&x,&val);
            x^=ans;val^=ans;
            tr2.root=tr2.insert(tr2.root,x-1,val);
        }
    }
    return 0;
}   

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

bzoj 3065: 带插入区间K小值 替罪羊树套主席树

省选的时候听来一个分块做法,就是按照询问Q每Q^0.5分一块然后块与块之间暴力重构。        或者用权值线段树套替罪羊树,具体可以见clj《重量平衡树和后缀平衡树在信息学中的应用》中一道例题。 ...

替罪羊树套线段树 【bzoj3065】 带插入区间k小值

题目大意: 维护一个序列。 支持以下操作: 1、查询区间k小值 2、修改一个值 3、插入一个值题目分析: 如果不带插入,主席树就可以搞定了。 带插入的话我们就既要维护权值大小,又要维护位...
  • Todobe
  • Todobe
  • 2017年03月13日 21:39
  • 192

bzoj 1901: Zju2112 Dynamic Rankings(带修改的区间第k大,树状数组+主席树)

果然在zju上交是要

浅谈主席树和区间第k值

主席树,是一种高级数据结构,是线段树的高级形式 主席树的全名应该叫做可持久化线段树 顾名思义,这种数据结构可以持久化,也即可查询历史记录 至于为什么要把这种数据结构叫做主席树,我就不多说了(我是...

主席树-区间第k大值(不带修改)

题目:poj2104 && hdu2665 题意:求区间第K大的值。 分析:资料1  主席树包含n棵线段树,这n棵线段树的形状完全相同。而且树与树之间有很大的重叠。 线段树root[i]表示数...
  • w20810
  • w20810
  • 2016年01月05日 12:55
  • 776

区间第k大 主席树代码

  • 2014年11月03日 16:06
  • 3KB
  • 下载

[bzoj3065]带插入区间K小值

题目大意在线维护一个序列,需要兹瓷插入、修改、求区间K小值。树套树开一颗权值线段树。 对于区间[l,r]对应的结点上保存一颗spaly,spaly中的结点权值均在[l,r],按照位置从小到大。 要...

[bzoj3065]带插入区间K小值 解题报告

喜闻乐见的用的很麻烦很傻逼得搞法。。 先用一个块链维护序列,做到O(n√)O(\sqrt n)插入,O(1)O(1)比较两个点在序列中位置的大小。时间复杂度是O(nn√)O(n\sqrt n)。 ...

bzoj3065带插入区间K小值

这题其实好像很难,但是听werkeytom_ftd说可以用块链水,于是就很开心地去打了个块状链表套主席树,插入操作就直接插到一个块中,注意如果块的大小2*block就将块分开,注意每一个修改或插入都要...

bzoj 3065 带插入区间k小值

我去,sb爆了。。。。二分的时候没考虑重复,tle了一天没找出来,又是这种神齐的小错误,555555。。。。 /********************************************...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:bzoj 3065 带插入区间K小值 重量平衡treap套主席树
举报原因:
原因补充:

(最多只允许输入30个字)