二进制分组学习笔记

二进制分组学习笔记

二进制分组,一种将较在线的东西,加一个 \(\log n\) 变成在线的技术。

具体来讲,就是你将元素的个数用二进制表示,比如 \(23 = 2^4 + 2^2 + 2^1 + 2^0\) ,那就有 4 个容器,分别放 16个元素,4个元素,2个元素,1个元素。每一次增加,就往后加一个 \(2^0\) 个元素的容器,然后往前合并。一般来说二进制分组的题目合并起来还是较简单的。最后达到一个 \(O(n\log nG(n))\) 的时间复杂度。 \(G(n)\) 表示合并的时间复杂度。

就以 luogu 普通平衡树来讲吧。

我们需要实现可以可以找到一个元素的排名。且可合并的容器,vector 其实就可以,这里的 vector 合并的时候需要有序 vector 用 \(O(len)\) 的时间复杂度来写。

查询小于等于一个数的数量的时间复杂度是 \(O(\log^2 n)\) 的。二进制分组+二分查找。

然后其实就可以了。这里求第 k 大需要二分加找到二进制分组内的小于等于这个数的数量,所以需要 \(O(\log^3 n)\)

代码:

#include<bits/stdc++.h>
using namespace std;
struct Bingroup {
	vector<vector<int>>vec;
	void insert(int x) {
		vector<int>tmp= {x};
		for(int i=0; i<vec.size(); i++) {
			if(!vec[i].size())
				return vec[i] = move(tmp),void();
			else {
				vector<int>nxt(tmp.size()+vec[i].size());
				merge(tmp.begin(),tmp.end(),vec[i].begin(),vec[i].end(),nxt.begin());
				tmp = move(nxt);
				vec[i]=vector<int>();
			}
		}
		vec.push_back(tmp); 
	}
	int count1(int x){
		int ans=0;
		for(int i=0;i<vec.size();i++)
			if(vec[i].size())ans+=lower_bound(vec[i].begin(),vec[i].end(),x)-vec[i].begin();
		return ans;	
	}
	//<
	int count2(int x){
		int ans=0;
		for(int i=0;i<vec.size();i++)
			if(vec[i].size())
				ans+=upper_bound(vec[i].begin(),vec[i].end(),x)-vec[i].begin();
		return ans;	
	}
	//<=
}add,del;
int rk(int x){
	return add.count1(x) - del.count1(x)+1; 
}
int kth(int x){
	int l=-1e7,r=1e7,ans=r;
	while(l<=r){
		int mid=l+r>>1;
		int cnt=add.count2(mid) - del.count2(mid); 
		if(cnt>=x)ans=mid,r=mid-1;
		else l=mid+1;
	}
	return ans;
}
int pre(int x){
	return kth(rk(x)-1); 
}
int suf(int x){
	return kth(add.count2(x)-del.count2(x)+1);
}
int main() {
	int T;
	cin>>T;
	while(T--){
		int op,l;
		cin>>op>>l;
		if(op==1)add.insert(l);
		else if(op==2)del.insert(l); 
		else if(op==3)cout<<rk(l)<<endl;
		else if(op==4)cout<<kth(l)<<endl;
		else if(op==5)cout<<pre(l)<<endl;
		else cout<<suf(l)<<endl;
	} 
	return 0;
}
posted @ 2026-04-23 15:39  hnczy  阅读(4)  评论(0)    收藏  举报