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

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

bzoj3065带插入区间K小值

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

[bzoj3065]带插入区间K小值

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

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

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

BZOJ3065 带插入区间K小值

因为要求支持插入,所以里层可以套上一个平衡树来维护对应位置的信息。 一般来说平衡树各项操作都是O(logN)的,但是由于外层要维护一个线段树,那么带旋转的平衡树复杂度就难以保证,因为每动一个节点就要...
  • u012076197
  • u012076197
  • 2016年01月22日 10:39
  • 852

hdu 5412 CRB and Queries(动态区间第k大值,区间能修改)(整体二分,树状数组套平衡树,线段树套treap)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5412 解题思路: 官方题解: In this problem, we can u...
  • piaocoder
  • piaocoder
  • 2015年08月25日 17:23
  • 1112

【 bzoj 3065 】 带插入区间K小值 - 树套树乱搞

这题写的真是excited。。。   虽然树套树这种东西随便嘴巴嘴巴就会了。。。但是写起来还是十分的蛋疼。。。   下午四点左右开始写,到将近七点写完,然后吃饭+思考人生了一小时,又开始debug...
  • GEOTCBRL
  • GEOTCBRL
  • 2016年02月04日 01:00
  • 637

主席树 求区间第k大数(可修改)

给出一个数列,以及多次操作,操作可以是修改某一个数值,也可以是询问某一段区间的...
  • u010089558
  • u010089558
  • 2014年07月24日 09:11
  • 1533

询问区间第k大(小)——主席树

例题 K-th Number You are working for Macrohard company in data structures department. After failing ...
  • samjia2000
  • samjia2000
  • 2015年08月13日 07:47
  • 1349

[BZOJ3196]二逼平衡树(线段树套splay)

花费了不知多少页来描述我们的心情 却不知为何填不满一行的空白
  • Clove_unique
  • Clove_unique
  • 2016年04月29日 09:59
  • 1694

【 bzoj 1500 】NOI2005 维修序列 - 平衡树乱搞 treap

嘛。。。又是屯了一年多的题。。。然而之前太弱了调不出来QAQ   随便一个可以提取区间的平衡树都可以搞这道题。   然后这题的splay我写过3个版本了。。。QAQ   为了写WC2016的T3...
  • GEOTCBRL
  • GEOTCBRL
  • 2016年02月02日 23:31
  • 1154
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:bzoj 3065 带插入区间K小值 重量平衡treap套主席树
举报原因:
原因补充:

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