树套树
树状数组,(动态开点线段树),平衡树
二逼平衡树
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
-
查询 \(k\) 在区间内的排名
-
查询区间内排名为 \(k\) 的值
-
修改某一位值上的数值
-
查询
在区间内的前驱(前驱定义为小于
,且最大的数) -
查询 \(k\) 在区间内的后继(后继定义为大于 \(k\),且最小的数)
-
区间问题
-
前驱后继
使用线段树处理区间,每个区间维护平衡树 \(O(N\log^2 N+M\log^3 N)\)
树状数组套主席树 \(O((N+M)\log^2 N)\)
两种数据需要开两个结构体
拆到两个数据结构结合起来,取他们的优点
平衡树天然动态开点
#include<bits/stdc++.h>
using namespace std;
const int N=50010;
const int V=1e8;
int n,m;
#define INF 0x3f3f3f3f
int tot;
constexpr int L1=16,L2=27;
struct SEG_Node{
int ls,rs,sum;
#define lc t[p].ls
#define rc t[p].rs
#define mid (L+R>>1)
}t[N*L1*L2];
int mknode(){
++tot;
return tot;
}
int a[N];
struct SEG{
int rt;
void pushup(int p){
t[p].sum = t[lc].sum+t[rc].sum;
}
void modify(int &p,int L,int R,int x,int v){
if(!p)p=mknode();
if(L==R){
t[p].sum += v;
return;
}
if(x<=mid)modify(lc,L,mid,x,v);
else modify(rc,mid+1,R,x,v);
pushup(p);
}
};
struct BIT{
SEG c[N];
#define lowbit(x) ((x)&-(x))
void change(int x,int v,int k){
if(x==0)return;
for(int i=x;i<=n;i+=lowbit(i))
c[i].modify(c[i].rt,0,V,v,k);
}
void find(int x,vector<int> &a){
for(;x;x-=lowbit(x))a.emplace_back(c[x].rt);
}
}bit;
int lsum(vector<int> a,vector<int> b){
int res=0;//可能一个节点会变空
for(auto x:a)if(x&&t[x].ls)res -= t[t[x].ls].sum;
for(auto x:b)if(x&&t[x].ls)res += t[t[x].ls].sum;
return res;
}
int rsum(vector<int> a,vector<int> b){
int res=0;//可能一个节点会变空
for(auto x:a)if(x&&t[x].rs)res -= t[t[x].rs].sum;
for(auto x:b)if(x&&t[x].rs)res += t[t[x].rs].sum;
return res;
}
void goL(vector<int> &a,vector<int> &b){
for(int i=0;i<a.size();++i)if(a[i])a[i]=t[a[i]].ls;
for(int i=0;i<b.size();++i)if(b[i])b[i]=t[b[i]].ls;
}
void goR(vector<int> &a,vector<int> &b){
for(int i=0;i<a.size();++i)if(a[i])a[i]=t[a[i]].rs;
for(int i=0;i<b.size();++i)if(b[i])b[i]=t[b[i]].rs;
}
int krank(int l,int r,int k){
vector<int> a,b;
a.clear();a.shrink_to_fit();
b.clear();b.shrink_to_fit();
bit.find(l-1,a);//当前的节点
bit.find(r,b);//当前的节点
int L=0,R=V;
int res=1;
while(L<=R){
if(L==R)return res;
if(k>mid)res += lsum(a,b);
if(k<=mid)goL(a,b),R=mid;
else goR(a,b),L=mid+1;
}
}
int kth(int l,int r,int k){
vector<int> a,b;
a.clear();a.shrink_to_fit();
b.clear();b.shrink_to_fit();
bit.find(l-1,a);//当前的节点
bit.find(r,b);//当前的节点
int L=0,R=V;
int res=0;
while(L<=R){
if(L==R)return L;
int tmp=lsum(a,b);
if(k<=tmp)R=mid,goL(a,b);
else L=mid+1,k-=tmp,goR(a,b);
}
}
int pre(int l,int r,int k){
int rk=krank(l,r,k);
return rk==1?-2147483647:kth(l,r,rk-1);
}
int suf(int l,int r,int k){
int rk=krank(l,r,k+1);
return rk==r-l+2?2147483647:kth(l,r,rk);
}
int main(){
ios::sync_with_stdio(0);
cin>>n>>m;
for(int i=1;i<=n;++i){
cin>>a[i];
bit.change(i,a[i],1);
}
for(int i=1;i<=m;++i){
int opt;cin>>opt;
if(opt==1){
int l,r,k;
cin>>l>>r>>k;
printf("%d\n",krank(l,r,k));
}else if(opt==2){
int l,r,k;
cin>>l>>r>>k;
printf("%d\n",kth(l,r,k));
}else if(opt==3){
int pos,k;cin>>pos>>k;
bit.change(pos,a[pos],-1);
a[pos]=k;
bit.change(pos,a[pos],1);
}else if(opt==4){
int l,r,k;
cin>>l>>r>>k;
printf("%d\n",pre(l,r,k));
}else{
int l,r,k;
cin>>l>>r>>k;
printf("%d\n",suf(l,r,k));
}
}
return 0;
}
K大数查询
权值线段树套线段树,线段树套权值线段树,都可以。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,m;
const int N=5e4+10,L=32;
constexpr long long V=N+N;
struct Seg_Node{
int ls,rs;
ll sum,laz;
#define lc(p) t[p].ls
#define rc(p) t[p].rs
#define mid (L+R>>1)
}t[N*L*L];
int tot;
struct Seg{
int rt,ls,rs;
int mknode(){
return ++tot;
}
void pushup(int p){
t[p].sum = t[lc(p)].sum+t[rc(p)].sum;
}
void op(int p,int v1,int v2){
t[p].sum += v1;
t[p].laz += v2;
}
void pushdown(int p,int L,int R){
if(!t[p].ls)t[p].ls=mknode();
if(!t[p].rs)t[p].rs=mknode();
if(t[p].laz){
op(lc(p),t[p].laz*(mid-L+1),t[p].laz);
op(rc(p),t[p].laz*(R-mid),t[p].laz);
t[p].laz=0;
}
}
void modify(int &p,int L,int R,int l,int r,int v){
if(!p)p=mknode();
if(l<=L&&R<=r){
op(p,(R-L+1)*v,v);
return;
}
pushdown(p,L,R);
if(l<=mid)modify(lc(p),L,mid,l,r,v);
if(r>mid)modify(rc(p),mid+1,R,l,r,v);
pushup(p);
}
ll query(int p,int L,int R,int l,int r){
if(rt==0)return 0;
if(l<=L&&R<=r)return t[p].sum;
pushdown(p,L,R);
ll res=0;
if(l<=mid)res+=query(lc(p),L,mid,l,r);
if(r>mid)res+=query(rc(p),mid+1,R,l,r);
return res;
}
}segt[N*L];
int TOT;
struct SEG{
int rt;
int mknode(){
return ++TOT;
}
void change(int &p,int L,int R,int x,int l,int r){
if(!p)p=mknode();
segt[p].modify(segt[p].rt,1,n,l,r,1);
// printf("rttrtrt %d %d\n",p,segt[p].rt);
if(L==R)return;
if(x<=mid)change(segt[p].ls,L,mid,x,l,r);
else change(segt[p].rs,mid+1,R,x,l,r);
}
int query(int p,int L,int R,ll x,int l,int r){
if(L==R)return L;
int Rs=segt[p].rs;
ll cntr=Rs?segt[Rs].query(segt[Rs].rt,1,n,l,r):0;
if(x<=cntr)return query(segt[p].rs,mid+1,R,x,l,r);
else return query(segt[p].ls,L,mid,x-cntr,l,r);
}
}Seg;
int main(){
cin>>n>>m;
for(int i=1;i<=m;++i){
int opt,l,r;ll c;
cin>>opt>>l>>r>>c;
if(opt==1)
Seg.change(Seg.rt,0,V,c+n,l,r);
else
cout<<Seg.query(Seg.rt,0,V,c,l,r)-n<<'\n';
}
return 0;
}
C++语言技巧
使用结构体
思想总结
问题的解决使用树套树的前提下,可以有多种解决方式,多种数据结构的套也许都能解决问题,那么就要进行综合复杂度分析。
树套树的本质是利用多种数树据结构的优势,比如第一题,既使用BIT进行区间查询,又使用树桶实现BST功能