二进制分组学习笔记
二进制分组学习笔记
二进制分组,一种将较在线的东西,加一个 \(\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;
}

浙公网安备 33010602011771号