zoukankan      html  css  js  c++  java
  • BZOJ3065 带插入区间K小值

    带插入区间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
    ",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;
    }
    
  • 相关阅读:
    C语言指针强制类型转换
    iphone开发实现单选框
    深入研究java.lang.Runtime类
    hoj 1760 The jackpot一维
    人生最有趣味的事
    GAE 博客——B3log Solo 0.3.0 正式版发布了!
    “三国演义”何处去
    boost库中的智能指针向上转换
    GAE 博客——B3log Solo 0.3.5 正式版发布了!
    GAE 博客——B3log Solo 0.3.0 正式版发布了!
  • 原文地址:https://www.cnblogs.com/autoint/p/10383048.html
Copyright © 2011-2022 走看看