其他分享
首页 > 其他分享> > BZOJ3065 带插入区间K小值

BZOJ3065 带插入区间K小值

作者:互联网

题意

从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i]。跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴。这时跳蚤国王决定理性愉悦一下,查询区间k小值。他每次向它的随从伏特提出这样的问题: 从左往右第x个到第y个跳蚤中,a[i]第k小的值是多少。
这可难不倒伏特,他在脑袋里使用函数式线段树前缀和的方法水掉了跳蚤国王的询问。
这时伏特发现有些跳蚤跳久了弹跳力会有变化,有的会增大,有的会减少。
这可难不倒伏特,他在脑袋里使用树状数组套线段树的方法水掉了跳蚤国王的询问。(orz 主席树)
这时伏特发现有些迟到的跳蚤会插入到这一行的某个位置上,他感到非常生气,因为……他不会做了。
请你帮一帮伏特吧。
快捷版题意:带插入、修改的区间k小值在线查询。

原序列长度 <= 35000

插入个数 <= 35000,修改个数 <= 70000,查询个数 <= 70000 ,0 <= 每时每刻的权值 <= 70000

分析

参照hzwer的题解。

得支持插入的树套树。由于没法旋转,所以只能选择替罪羊树。

学习了一下替罪羊树,具体参见WJMZBMR《重量平衡树和后缀平衡树在信息学奥赛中的应用》。维基百科上面有证明。

替罪羊树是一种不用旋转的平衡树,若一棵子树的左或右子树大小超过其大小55%-80%则暴力重构这棵子树,以此来维护平衡性,每个结点的期望重构次数是logn,实现可以自己脑补一下

在替罪羊树每个结点放一棵包含该子树所有结点的权值线段树,也就是平衡树套权值线段树

  1. 由于外层是平衡树,那么就能实现插入一个结点:找到它的位置,在根到其路径上所有结点的线段树中插入这个值
  2. 查询区间第K大:找到这个区间包含若干棵子树,拿出他们的根的权值线段树,一起做个二分
  3. 修改与插入类似
  4. 当外层平衡树失衡的时候重构之。

由于内存不够,我们还需要回收垃圾,即对数组的重复使用

时间复杂度

  1. 查询区间第K大:我觉得把那些节点提取出来就是一个玄学操作,复杂度?据洛谷管理员noip说是\(O(\log n)\)的,那么查第k大就是\(O(\log^2n)\)的。但是我个人感觉这不是正确的上界。
  2. 重构:由于要回收节点,所以共用节点很麻烦,能力限制使我不能写线段树合并。但是暴力重构的复杂度还是\(O(\log^2 n)\)的。

总时间复杂度\(O((n+m) \log^2 n)\),但是常数问题导致我过不了洛谷上面时限1s的题。

代码

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;
    rg char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
        data=data*10+ch-'0',ch=getchar();
    return data*w;
}
template<class T>il T read(rg T&x){
    return x=read<T>();
}
typedef long long ll;
using std::vector;

co int N=7e4+1,LG=1e7; // memory limit
int n,m;
// Tree Tao Tree
namespace T{
    // Interval Tree
    int tot,bin[LG];
    int L[LG],R[LG],sum[LG];
    il int construct() {return bin[0]?bin[bin[0]--]:++tot;}
    il void destruct(int&x){
        if(!x) return;
        destruct(L[x]),destruct(R[x]);
        bin[++bin[0]]=x,sum[x]=0,x=0; // edit 2: x=0
    }
    void insert(int&x,int l,int r,int p,int d){
        if(!x) x=construct();
        sum[x]+=d;
        if(!sum[x]){
            destruct(x);
            return;
        }
        if(l==r) return;
        int m=(l+r)/2;
        if(p<=m) insert(L[x],l,m,p,d);
        else insert(R[x],m+1,r,p,d);
    }
    int query(vector<int>&x,co vector<int>&vs,int l,int r,int k){
        if(l==r) return l;
        int s=0,m=(l+r)/2;
        for(rg unsigned i=0;i<x.size();++i)
            s+=sum[L[x[i]]];
        for(rg unsigned i=0;i<vs.size();++i)
            s+=(l<=vs[i]&&vs[i]<=m);
        if(s>=k){
            for(rg unsigned i=0;i<x.size();++i)
                x[i]=L[x[i]];
            return query(x,vs,l,m,k);
        }
        else{
            for(rg unsigned i=0;i<x.size();++i)
                x[i]=R[x[i]];
            return query(x,vs,m+1,r,k-s);
        }
    }
    // Scapegoat Tree
    co double ratio=0.75; // edit 3:double
    int root,pot[N];
    int val[N],tree[N],ch[N][2];
    void build(int&t,int l,int r){
        if(l>r) return;
        int m=(l+r)/2;
        t=pot[m];
        for(rg int i=l;i<=r;++i)
            insert(tree[t],0,7e4,val[pot[i]],1);
        if(l==r) return;
        build(ch[t][0],l,m-1),build(ch[t][1],m+1,r); // edit 1:m-1 for balanced tree
    }
    void split(int t,int l,int r,vector<int>&x,vector<int>&vs){
        int sl=sum[tree[ch[t][0]]],st=sum[tree[t]];
        if(l==1&&r==st){
            x.push_back(tree[t]);
            return;
        }
        if(l<=sl+1&&sl+1<=r) vs.push_back(val[t]);
        if(r<=sl) split(ch[t][0],l,r,x,vs);
        else if(l>sl+1) split(ch[t][1],l-sl-1,r-sl-1,x,vs); // edit 3: 1,not 0
        else{
            if(l<=sl) split(ch[t][0],l,sl,x,vs);
            if(r>sl+1) split(ch[t][1],1,r-sl-1,x,vs);
        }
    }
    int query(int t,int l,int r,int k){
        vector<int> x,vs;
        split(t,l,r,x,vs);
        return query(x,vs,0,7e4,k);
    }
    int modify(int t,int k,int v){
        insert(tree[t],0,7e4,v,1);
        int o,sl=sum[tree[ch[t][0]]];
        if(sl+1==k) o=val[t],val[t]=v;
        else if(sl>=k) o=modify(ch[t][0],k,v);
        else o=modify(ch[t][1],k-sl-1,v);
        insert(tree[t],0,7e4,o,-1);
        return o;
    }
    void remove(int&t){
        if(!t) return;
        remove(ch[t][0]);
        pot[++pot[0]]=t;
        remove(ch[t][1]);
        destruct(tree[t]),t=0; // edit 2:t=0
    }
    il void rebuild(int&t){
        remove(t);
        build(t,1,pot[0]);
        pot[0]=0;
    }
    int tmp;
    void insert(int&t,int k,int v){
        if(!t){
            t=++n;
            val[t]=v,insert(tree[t],0,7e4,v,1);
            return;
        }
        insert(tree[t],0,7e4,v,1);
        int sl=sum[tree[ch[t][0]]];
        if(sl>=k) insert(ch[t][0],k,v);
        else insert(ch[t][1],k-sl-1,v);
        if(sum[tree[t]]*ratio>std::max(sum[tree[ch[t][0]]],sum[tree[ch[t][1]]])){
            if(tmp){ // edit 4: when rebuilding must change ch as well
                rebuild(ch[t][tmp==ch[t][1]]);
                tmp=0;
            }
        }
        else tmp=t;
    }
}

int main(){
//  freopen("BZOJ3065.in","r",stdin);
//  freopen("BZOJ3065.out","w",stdout);
    read(n);
    for(rg int i=1;i<=n;++i)
        read(T::val[i]),T::pot[i]=i;
    T::build(T::root,1,n);
    read(m);
    int lastans=0;
    while(m--){
        char opt[2];
        scanf("%s",opt);
        switch(opt[0]){
            case 'Q':{
                int l=read<int>()^lastans,r=read<int>()^lastans,k=read<int>()^lastans;
                printf("%d\n",lastans=T::query(T::root,l,r,k));
                break;
            }
            case 'M':{
                int k=read<int>()^lastans,v=read<int>()^lastans;
                T::modify(T::root,k,v);
                break;
            }
            case 'I':{
                int k=read<int>()^lastans,v=read<int>()^lastans;
                T::insert(T::root,k-1,v);
                if(T::tmp){
                    T::rebuild(T::root);
                    T::tmp=0;
                }
                break;
            }
            default:assert(0);
        }
    }
    return 0;
}

标签:BZOJ3065,ch,return,int,sum,tree,插入,小值,sl
来源: https://www.cnblogs.com/autoint/p/10383048.html