块状链表
块状链表的结构很简单:就是将数组划分为若干块,块之间用链表连接,你可以理解为块可以分裂和合并的分块。
块状链表有两个核心操作 split 和 merge 用于保证块的大小在 \(O(\sqrt n)\) 级别。具体方法就是:设块长为 \(B\),若某块内元素数量大于 \(2B\) 就从中间分裂成相邻两块,如果相邻两块的元素数量之和小于 \(2B\) 就合并两块。
这里我偷懒了,直接用的 vector 套 vector,常数略大但是最坏复杂度是不变的。
平衡树
在模板题中打爆了以前我写过的所有平衡树。
constexpr int block=500,inf=2e9;
struct BlockList{
vector<vector<int>> b;
void split(size_t idx){
if(b[idx].size()>2*block){
size_t mid=b[idx].size()/2;
vector<int> temp;
temp.insert(temp.begin(),b[idx].begin()+mid,b[idx].end());
b[idx].resize(mid);
b.insert(b.begin()+idx+1,temp);
}
}
void merge(size_t idx){
if(b[idx].empty()&&b.size()>1){
b.erase(b.begin()+idx);
return;
}
if(idx+1<b.size()&&b[idx].size()+b[idx+1].size()<block*2){
b[idx].insert(b[idx].end(),b[idx+1].begin(),b[idx+1].end());
b.erase(b.begin()+idx+1);
}
}
BlockList(){b.push_back({});}
void insert(int x){
for(size_t i=0;i<b.size();i++)
if(!b[i].empty()&&x<b[i].back()){
auto it=lower_bound(b[i].begin(),b[i].end(),x);
b[i].insert(it,x);
split(i);
return;
}
if(b.back().size()<2*block) b.back().push_back(x);
else b.push_back({x});
}
void erase(int x){
for(size_t i=0;i<b.size();i++)
if(x<=b[i].back()){
auto it=lower_bound(b[i].begin(),b[i].end(),x);
if(*it!=x) return;
b[i].erase(it);
merge(i);
return;
}
}
size_t rnk(int x){
size_t cnt=0;
for(size_t i=0;i<b.size();i++){
if(b[i].empty()) continue;
if(b[i].back()<x) cnt+=b[i].size();
else{
auto it=lower_bound(b[i].begin(),b[i].end(),x);
cnt+=it-b[i].begin();
return cnt+1;
}
}
return cnt+1;
}
size_t kth(size_t x){
size_t cnt=0;
for(size_t i=0;i<b.size();i++){
if(cnt+b[i].size()>=x)
return b[i][x-cnt-1];
cnt+=b[i].size();
}
return -1;
}
int pre(int x){
int res=-inf;
for(size_t i=0;i<b.size();i++){
if(b[i].empty()) continue;
if(b[i].back()<x) res=b[i].back();
else{
auto it=lower_bound(b[i].begin(),b[i].end(),x);
if(it!=b[i].begin())
res=*--it;
return res;
}
}
return res;
}
int nxt(int x){
int res=inf;
for(size_t i=0;i<b.size();i++){
auto it=upper_bound(b[i].begin(),b[i].end(),x);
if(it!=b[i].end()){
res=*it;
return res;
}
}
return res;
}
}rope;
文艺平衡树
对于区间翻转问题,可以将涉及的块全部分裂出来然后整块翻转,然后合并防止复杂度退化(话说我觉得定期重构应该比这个好写而且跑的快)。
constexpr int block=350;
struct BlockList{
vector<vector<int>> b;
vector<char> tag;
BlockList(){b.push_back({});tag.push_back(0);}
inline void push_down(size_t idx){
if(!tag[idx]) return;
reverse(b[idx].begin(),b[idx].end());
tag[idx]=0;
}
void merge(size_t idx){
if(idx+1>=b.size()) return;
if(b[idx].size()+b[idx+1].size()<block*2){
push_down(idx),push_down(idx+1);
b[idx].insert(b[idx].end(),b[idx+1].begin(),b[idx+1].end());
b.erase(b.begin()+idx+1);
tag.erase(tag.begin()+idx+1);
}
}
size_t split_pos(size_t pos){
size_t cnt=0;
for(size_t i=0;i<b.size();i++){
if(cnt+b[i].size()>pos){
push_down(i);
size_t idx=pos-cnt;
if(idx==0) return i;
vector<int> temp;
temp.assign(b[i].begin()+idx,b[i].end());
b[i].resize(idx);
b.insert(b.begin()+i+1,temp);
tag.insert(tag.begin()+i+1,0);
return i+1;
}
cnt+=b[i].size();
if(cnt==pos) return i+1;
}
return b.size();
}
inline size_t bel(size_t x){
size_t cnt=0;
for(size_t i=0;i<b.size();i++)
if(cnt+b[i].size()>=x) return i;
else cnt+=b[i].size();
return -1;
}
void insert(int x){
if(b.back().size()<block) b.back().push_back(x);
else b.push_back({x}),tag.push_back(0);
}
void rev(int l,int r){
size_t x=split_pos(l),y=split_pos(r+1);
reverse(b.begin()+x,b.begin()+y);
reverse(tag.begin()+x,tag.begin()+y);
for(size_t i=x;i<y;i++) tag[i]^=1;
if(y>0) merge(y-1);
if(x>0) merge(x-1);
}
void print(){
for(size_t i=0;i<b.size();i++){
push_down(i);
for(int x:b[i]) cout<<x<<' ';
}
}
}rope;

浙公网安备 33010602011771号