1.3闲话

发布时间 2024-01-03 21:25:45作者: Vsinger_洛天依

昨天晚上班级内部大换宿舍但是我宿舍没变,只是从左窗上平移到右窗上非常好

推歌:蝶变\洛天依

由二逼平衡树引发了一些树套树的学习

首先看一道题目

这是一道模板题。

您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:

  1. 查询 \(x\) 在区间内的排名

  2. 查询区间内排名为 \(k\) 的值

  3. 修改某一位置上的数值

  4. 查询 \(x\) 在区间内的前驱(前驱定义为小于 \(x\) ,且最大的数)

  5. 查询 \(x\) 在区间内的后继(后继定义为大于 \(x\) ,且最小的数)

查询前驱/后继可以用平衡树秒了

修改某一位置上的数值可以平衡树直接del然后再ins即可

查询全局排名为 \(k\) 的值可以用平衡树解决,但是区间肯定不能裸平衡树非常不好

查询区间 \(x\) 在全局的排名可以用平衡树解决,但是区间肯定也不能裸平衡树非常恼

那么有一个显而易见的做法

那就是用一大堆平衡树维护

我们可以维护一个线段树,让线段树的每个节点都是一颗平衡树即可解决非常好

那么我们就解决了这道二逼平衡树模板,注意一些细节,比如最开始我开了\(1e6\)棵平衡树直接似了

点击查看代码
#include<bits/stdc++.h>
#define int long long
namespace IO{
    inline void close(){std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);}
    inline void Fire(){freopen("data.in","r",stdin);freopen("data.out","w",stdout);}
    inline int read(){int s = 0,w = 1;char ch = getchar();while(ch<'0'||ch>'9'){ if(ch == '-') w = -1;ch = getchar();}while(ch>='0'&&ch<='9'){ s = s*10+ch-'0';ch = getchar();}return s*w;}
    inline void write(int x){char F[200];int tmp=x>0?x:-x,cnt=0;;if(x<0)putchar('-') ;while(tmp>0){F[cnt++]=tmp%10+'0';tmp/=10;}while(cnt>0)putchar(F[--cnt]);}
}
#define close IO::close
#define Fire IO::Fire
#define read IO::read
#define write IO::write
#define cin std::cin
#define cout std::cout
#define endl '\n'
#define min(a,b) ((a)<(b))?(a):(b)
#define max(a,b) ((a)>(b))?(a):(b)
const int N=1000005;
const int INF=(1ll<<31)-1;
int a[N],mx=-INF,ret;
class Spaly{
private:
    int f[N],ch[N][2];
public:
    int key[N],lazytag=0,sz,root[N],totf=0,cnt[N],siz[N];
    inline void clear(int x){
        ch[x][0]=ch[x][1]=f[x]=cnt[x]=key[x]=siz[x]=0; 
    }
    inline int get(int x){
        return ch[f[x]][1]==x;
    }
    inline void update(int x){
        if(x){
            siz[x]=cnt[x];
            if(ch[x][0])
                siz[x]+=siz[ch[x][0]];
            if(ch[x][1])
                siz[x]+=siz[ch[x][1]];
        }
    }
    inline void rotate(int x){
        int old=f[x],oldf=f[old],which=get(x);
        ch[old][which]=ch[x][which^1];
        f[ch[old][which]]=old;
        ch[x][which^1]=old;
        f[old]=x;f[x]=oldf;
        if(oldf)
            ch[oldf][ch[oldf][1]==old]=x;
        update(old);
        update(x);
    }
    inline void splay(int x){
        for(int fa;fa=f[x];rotate(x))
            if(f[fa])
                rotate(get(x)==get(fa)? fa : x);
    }
    inline void insert(int idx,int v){
        if(root[idx]==0){
            ++sz;
            root[idx]=sz;
            ch[root[idx]][0]=ch[root[idx]][1]=f[root[idx]]=0;
            key[root[idx]]=v;
            cnt[root[idx]]=siz[root[idx]]=1;
            return;
        }
        int cur=root[idx],fa=0;
        while(1){
            if(key[cur]==v){
                ++cnt[cur];
                update(fa);
                splay(cur);
                root[idx]=cur;
                return;
            }
            fa=cur;
            cur=ch[cur][key[cur]<v];
            if(cur==0){
                ++sz;
                ch[sz][0]=ch[sz][1]=0;
                key[sz]=v;
                siz[sz]=1;
                cnt[sz]=1;
                f[sz]=fa;
                ch[fa][key[fa]<v]=sz;
                update(fa);
                splay(sz);
                root[idx]=sz;
                return;
            }
        }
    }
    void FIND(int idx,int x) {
        int now=root[idx];
        while(1){
            if(key[now]==x) {
                splay(now);
                root[idx]=now;
                return ;
            }
            now=ch[now][key[now]<x];
        }
    }
    int findth(int idx,int x) {
        int now=root[idx],ans=0;
        while(1){
            if(!now) return ans;
            if(key[now]==x) return ans+(ch[now][0]?siz[ch[now][0]]:0);
            if(key[now]<x) ans+=(ch[now][0]?siz[ch[now][0]]:0)+cnt[now];
            now=ch[now][key[now]<x];
        }
    }
    inline int pre(int idx){
        int cur=ch[root[idx]][0];
        while(ch[cur][1])
            cur=ch[cur][1];
        return cur;
    }
    inline int nxt(int idx){
        int cur=ch[root[idx]][1];
        while(ch[cur][0])
            cur=ch[cur][0];
        return cur;
    }
    inline void del(int v){
        if(cnt[root[v]]>1){
            --cnt[root[v]];
            update(root[v]);
            return;
        }
        if(!ch[root[v]][0] && !ch[root[v]][1]){
            clear(root[v]);
            root[v]=0;
            return;
        }
        if(!ch[root[v]][0]){
            int oldroot=root[v];
            root[v]=ch[oldroot][1];
            f[root[v]]=0;
            clear(oldroot);
            return;
        }
        else if(!ch[root[v]][1]){
            int oldroot=root[v];
            root[v]=ch[root[v]][0];
            f[root[v]]=0;
            clear(oldroot);
            return;
        }
        int lpre=pre(v),oldroot=root[v];
        splay(lpre);root[v]=lpre;
        f[ch[oldroot][1]]=root[v];
        ch[root[v]][1]=ch[oldroot][1];
        update(root[v]);
        return;
    }
    inline int PRE(int idx,int x){
        int cur=root[idx],ans=-INF;
        while(cur){
            if(key[cur]<x) {
                ans=max(ans,key[cur]);
                cur=ch[cur][1];
            }
            else cur=ch[cur][0];
        }
        return ans;
    }
    inline int NXT(int idx,int x){
        int cur=root[idx],ans=INF;
        while(cur){
            if(key[cur]>x){
                ans=min(ans,key[cur]);
                cur=ch[cur][0];
            }
            else cur=ch[cur][1];
        }
        return ans;
    }
}TrEE;
class ST{
public:
    #define lc q<<1
    #define rc q<<1|1
    #define mid ((l+r)>>1)
    void insert(int q,int l,int r,int x,int v) {
        TrEE.insert(q,v);
        if(l==r) return;
        if(x<=mid) insert(lc,l,mid,x,v);
        else insert(rc,mid+1,r,x,v);
        return ;
    }
    int Askrank(int q,int l,int r,int ql,int qr,int x){
        if(ql>r || qr<l) 
            return 0;
        if(l>=ql&&r<=qr)
            return TrEE.findth(q,x);
        // if(mid>=ql) ;
        // if(mid<qr) ans+=;
        return Askrank(lc,l,mid,ql,qr,x)+Askrank(rc,mid+1,r,ql,qr,x);
    }
    void change(int q,int l,int r,int pos,int k) {
        TrEE.FIND(q,a[pos]);
        TrEE.del(q);
        TrEE.insert(q,k);
        if(l==r) return;
        if(pos<=mid) change(lc,l,mid,pos,k);
        else change(rc,mid+1,r,pos,k);
        return ;
    }
    int Pre(int q,int l,int r,int ql,int qr,int k) {
        if(ql<=l&&r<=qr) 
            return TrEE.PRE(q,k);
        int ans=-INF;
        if(mid>=ql)	ans=max(ans,Pre(lc,l,mid,ql,qr,k));
        if(mid<qr) ans=max(ans,Pre(rc,mid+1,r,ql,qr,k));
        return ans;
    }
    int Nxt(int q,int l,int r,int ql,int qr,int k) {
        if(ql<=l&&r<=qr)
            return TrEE.NXT(q,k);
        int ans=INF;
        if(mid>=ql)	ans=min(ans,Nxt(lc,l,mid,ql,qr,k));
        if(mid<qr) ans=min(ans,Nxt(rc,mid+1,r,ql,qr,k));
        return ans;
    }
}ST;
signed main(){
    // Fire();
    int n=read(),m=read(),qwq=m;
	for(int i=1;i<=n;i++) {
		a[i]=read();mx=max(mx,a[i]);
		ST.insert(1,1,n,i,a[i]);
	}
	while(m--) {
		int opt=read(),l,r,pos,k;
		if(opt==1) {
			l=read(),r=read(),k=read();
			int res=ST.Askrank(1,1,n,l,r,k);
			printf("%d\n",res+1);
		}
		if(opt==2) {
			l=read(),r=read(),k=read();
			int L=0,R=mx,ret;
			while(L<=R) {
				int MID=(L+R)>>1;
				int res=ST.Askrank(1,1,n,l,r,MID);
				if(res<k) {
					L=MID+1;
					ret=MID;
				}
				else R=MID-1;
			}
			printf("%d\n",ret);
		}
		if(opt==3) {
			pos=read(),k=read();
			ST.change(1,1,n,pos,k);
			mx=max(mx,k);
			a[pos]=k;
		}
		if(opt==4) {
			l=read(),r=read(),k=read();
			int res=ST.Pre(1,1,n,l,r,k);
			printf("%d\n",res);
		}
		if(opt==5) {
			l=read(),r=read(),k=read();
			int res=ST.Nxt(1,1,n,l,r,k);
			printf("%d\n",res);
		}
	}
	return 0;
}

然后我一晚上就写了这一道题剩下的咕咕咕了

非常好,两百行居然打住了

但是下一道题我就一点都看不懂了悲悲悲

要不先练一下基础的树套树和splay再看后面几道题