zoukankan      html  css  js  c++  java
  • [BZOJ]3065: 带插入区间K小值

    题目大意:一个长度为n的序列,支持三种操作:1.查询区间k小值;2.修改一个元素;3.插入一个元素;强制在线。(n<=35000,插入操作数<=35000,修改操作数<=70000,查询操作数<=70000,0<=数字大小<=70000,4个点,总时限60s)

    思路:做法比较多,我写的是权值线段树套平衡树,平衡树内维护该权值区间内各元素位置,每次查询从线段树根结点开始左右走就可以了。现在问题是如何解决带插入的情况下快速比较两个位置标号的前后关系,我们可以用替罪羊树维护,我们给每个位置标号定个rank区间,例如根定为[0,1e18],定其rank值为中点5*1e17,那么他左儿子的rank区间为[0,5*1e17],右儿子为[5*1e17,1e18],这样我们每次只要O(1)就可以比较两个位置标号的前后关系,我们用double存这个rank,如果树深度太高,rank精度可能不够,我们重构替罪羊树时一并重构rank即可解决。总复杂O((n+q)*log^2),这个代码跑了28s左右。

    #include<cstdio>
    inline int read()
    {
        int x;char c;
        while((c=getchar())<'0'||c>'9');
        for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0';
        return x;
    }
    #define MN 70000
    #define N 131072
    #define LG 17
    #define ND LG*MN
    struct sgt
    {
        static const double alpha=0.75;
        int a[MN+5],rt,lc[MN+5],rc[MN+5],s[MN+5],cnt,*rb;
        double lh[MN+5],rh[MN+5],rk[MN+5];
        int build(int l,int r,double lx,double rx)
        {
            if(l>r)return 0;
            int mid=l+r>>1,x=a[mid];s[x]=r-l+1;
            rk[x]=((lh[x]=lx)+(rh[x]=rx))/2;
            lc[x]=build(l,mid-1,lx,rk[x]);
            rc[x]=build(mid+1,r,rk[x],rx);
            return x;
        }
        void ins(int*x,int z,int k,double lx,double rx)
        {
            if(!*x){rk[*x=z]=((lh[z]=lx)+(rh[z]=rx))/2;s[z]=1;return;}
            ++s[*x];
            if(k<s[lc[*x]]+2)ins(lc+*x,z,k,lx,rk[*x]);
            else ins(rc+*x,z,k-s[lc[*x]]-1,rk[*x],rx);
            if(s[lc[*x]]>s[*x]*alpha||s[rc[*x]]>s[*x]*alpha)rb=x;
        }
        void dfs(int x){if(x)dfs(lc[x]),a[++cnt]=x,dfs(rc[x]);}
        void insert(int x,int k)
        {
            rb=0;ins(&rt,x,k,1,1e18);
            if(rb)cnt=0,dfs(*rb),*rb=build(1,cnt,lh[*rb],rh[*rb]);
        }
        int find(int k)
        {
            for(int x=rt;;)
                if(k<=s[lc[x]])x=lc[x];
                else if(k-=s[lc[x]]+1)x=rc[x];
                else return x;
        }
    }sgt;
    int a[MN+5],rt[N*2+5],d[ND+5];
    namespace treap
    {
        int fa[ND+5],c[ND+5][2],s[ND+5],p[ND+5];
        inline int cmp(int x,int y){return sgt.rk[d[x]]<sgt.rk[d[y]];}
        inline int ran()
        {
            static int x=23333;
            return x^=x<<13,x^=x>>17,x^=x<<5;
        }
        inline void up(int x){s[x]=s[c[x][0]]+s[c[x][1]]+1;}
        void rotate(int x)
        {
            int f=fa[x],ff=fa[f],l=c[f][1]==x,r=l^1;
            fa[f]=x;fa[x]=ff;fa[c[x][r]]=f;
            (ff>0?c[ff][c[ff][1]==f]:rt[-ff])=x;c[f][l]=c[x][r];c[x][r]=f;
            up(f);up(x);
        }
        void ins(int&x,int f,int z)
        {
            if(!x)
            {
                fa[x=z]=f;s[z]=1;p[z]=ran();
                while(fa[z]>0&&p[z]>p[fa[z]])rotate(z);
                return;
            }
            ++s[x];ins(c[x][cmp(x,z)],x,z);
        }
        void del(int x)
        {
            while(c[x][0]||c[x][1])rotate(c[x][p[c[x][0]]<p[c[x][1]]]);
            if(fa[x]<0){rt[-fa[x]]=0;return;}
            c[fa[x]][c[fa[x]][1]==x]=0;
            for(int i=x;(i=fa[i])>0;)up(i);
        }
        int sum(int x,int z,int f)
        {
            if(!x)return 0;
            if(f?cmp(x,z):!cmp(z,x))return s[c[x][0]]+1+sum(c[x][1],z,f);
            return sum(c[x][0],z,f);
        }
    };
    void ins(int k,int x)
    {
        for(int i=(k+=N,0);i<LG;++i,k>>=1)
            if(~k&1)treap::ins(rt[k],-k,i*MN+x);
    }
    void del(int k,int x)
    {
        for(int i=(k+=N,0);i<LG;++i,k>>=1)
            if(~k&1)treap::del(i*MN+x);
    }
    int query(int k,int l,int r,int x)
    {
        if(k>=N)return k-N;
        int lk=treap::sum(rt[k<<1],r,0)-treap::sum(rt[k<<1],l,1);
        if(x<=lk)return query(k<<1,l,r,x);
        return query(k<<1|1,l,r,x-lk);
    }
    int main()
    {
        int n=read(),i,j,x,y;char s[5];
        for(i=1;i<=n;++i)sgt.a[i]=i;
        sgt.rt=sgt.build(1,n,1,1e18);
        for(i=0;i<LG;++i)for(j=1;j<=MN;++j)d[i*MN+j]=j;
        treap::p[0]=0x80000000;
        for(i=1;i<=n;++i)ins(a[i]=read(),i);
        for(i=read(),j=0;i--;)
        {
            scanf("%s",s);x=read()^j;y=read()^j;
            if(s[0]=='Q')printf("%d
    ",j=query(1,sgt.find(x),sgt.find(y),read()^j));
            if(s[0]=='M')x=sgt.find(x),del(a[x],x),ins(a[x]=y,x);
            if(s[0]=='I')sgt.insert(++n,x),ins(a[n]=y,n);
        }
    }
  • 相关阅读:
    Flume线上日志采集【模板】
    【转】什么叫众筹?什么叫大数据?什么叫互联网思维?简单粗暴秒懂!
    【转】搞清楚LzoCodec和LzopCodec
    linux tail -f 和 tail -F的区别 && tail 的断点续传
    hadoop输出lzo文件并添加索引
    中文转Punycode
    Storm学习笔记——简介
    HBase学习笔记——Java API操作
    HBase学习笔记——配置及Shell操作
    HBase学习笔记——概念及原理
  • 原文地址:https://www.cnblogs.com/ditoly/p/BZOJ3065.html
Copyright © 2011-2022 走看看