bzoj3065 带插入区间K小值(替罪羊树套动态开点线段树)

替罪羊树呢就是不用旋转的平衡树,那不旋转如何维持平衡呢?我们设定一个 α 值,每当一个点不满足大小平衡时我们就暴力重构那一部分。用平衡树维护区间位置,每个节点一棵权值线段树。查询的时候我们就把x~y区间搞出来(用一堆权值线段树和单点表示),然后类似主席树求第k小的去树上二分找一下即可。此题还要回收垃圾。。。因为内存不够!删除还有回收的时候记得传引用,因为你还要断掉他父亲和他的边。重构的时候只重构深度最小的那个结点的子树即可。复杂度 O(nlog3n)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 70010
#define alpha 0.75
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}char op[5];
inline void read_S(){
    int l=0;char ch=gc();
    while(ch<'A'||ch>'Z') ch=gc();
    while(ch>='A'&&ch<='Z') op[++l]=ch,ch=gc(); 
}
int n,v[N],dfn[N],rt[N],lc[N],rc[N],root=0,ans=0,owo=0,tmp,dfnum=0;
struct node{int lc,rc,sum;}tr[10000010];
queue<int>rec;
inline int newnode(){
    if(rec.empty()) return ++owo;
    int x=rec.front();rec.pop();return x;
}
inline void dorec(int &p){
    if(!p) return;rec.push(p);
    dorec(tr[p].lc);dorec(tr[p].rc);tr[p].sum=0;p=0;
}
inline void add(int &p,int l,int r,int x,int val){
    if(!p) p=newnode();tr[p].sum+=val;
    if(l==r) return;int mid=l+r>>1;
    if(x<=mid) add(tr[p].lc,l,mid,x,val);
    else add(tr[p].rc,mid+1,r,x,val);
    if(!tr[p].sum) dorec(p);
}
inline void build(int &p,int l,int r){
    int mid=l+r>>1;p=dfn[mid];
    for(int i=l;i<=r;++i) add(rt[p],0,70000,v[dfn[i]],1);
    if(l<mid) build(lc[p],l,mid-1);
    if(r>mid) build(rc[p],mid+1,r);
}
vector<int>a,a1;
inline void gao(int p,int x,int y){
    int szl=tr[rt[lc[p]]].sum,sz=tr[rt[p]].sum;
    if(x==1&&y==sz){a.push_back(rt[p]);return;}
    if(y<=szl){gao(lc[p],x,y);return;}
    if(x>szl+1){gao(rc[p],x-szl-1,y-szl-1);return;}
    a1.push_back(v[p]);
    if(x<=szl) gao(lc[p],x,szl);
    if(y>szl+1) gao(rc[p],1,y-szl-1);
}
inline void query(int x,int y,int k){
    a.clear();a1.clear();
    gao(root,x,y);int l=0,r=70000;
    while(l<=r){
        if(l==r) break;int sum=0,mid=l+r>>1;
        for(int i=0;i<a.size();++i){
            int p=a[i];
            sum+=tr[tr[a[i]].lc].sum;
        }
        for(int i=0;i<a1.size();++i) sum+=(l<=a1[i]&&a1[i]<=mid);
        if(k<=sum){
            for(int i=0;i<a.size();++i) a[i]=tr[a[i]].lc;r=mid;
        }else{
            for(int i=0;i<a.size();++i) a[i]=tr[a[i]].rc;l=mid+1,k-=sum;
        }
    }printf("%d\n",ans=l);
}
inline int modify(int p,int x,int val){
    add(rt[p],0,70000,val,1);int szl=tr[rt[lc[p]]].sum,t;
    if(szl+1==x) t=v[p],v[p]=val;
    else if(x<=szl) t=modify(lc[p],x,val);
    else t=modify(rc[p],x-szl-1,val);
    add(rt[p],0,70000,t,-1);return t;
}
int *stack[N],top;
inline void ins(int &p,int x,int val){
    if(!p){
        p=++n;v[p]=val;add(rt[p],0,70000,val,1);return;
    }int szl=tr[rt[lc[p]]].sum;add(rt[p],0,70000,val,1);
    if(x<=szl+1){
        ins(lc[p],x,val);
        if(tr[rt[p]].sum*alpha<tr[rt[lc[p]]].sum) stack[++top]=&p;
    }else{
        ins(rc[p],x-szl-1,val);
        if(tr[rt[p]].sum*alpha<tr[rt[rc[p]]].sum) stack[++top]=&p;
    }
}
inline void del(int &p){
    if(!p) return;dorec(rt[p]);
    del(lc[p]);dfn[++dfnum]=p;del(rc[p]);p=0;
}
inline void rebuild(int &p){
    dfnum=0;del(p);build(p,1,dfnum);
}
inline void insert(int x,int val){//在x前面插入val
    top=0;ins(root,x,val);
    if(top) rebuild(*stack[top]);
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();
    for(int i=1;i<=n;++i) v[i]=read(),dfn[i]=i;
    build(root,1,n);int m=read();
    while(m--){
        read_S();
        int x=read()^ans,y=read()^ans;
        if(op[1]=='Q') query(x,y,read()^ans);
        else if(op[1]=='M') modify(root,x,y);
        else insert(x,y);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值