• 【洛谷P3380】【模板】二逼平衡树(树套树)


    题目

    题目链接:https://www.luogu.com.cn/problem/P3380
    您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:

    1. 查询 (k) 在区间内的排名。
    2. 查询区间内排名为 (k) 的值。
    3. 修改某一位值上的数值。
    4. 查询 (k) 在区间内的前驱(前驱定义为严格小于 (x),且最大的数,若不存在输出 (-2147483647))。
    5. 查询 (k) 在区间内的后继(后继定义为严格大于 (x),且最小的数,若不存在输出 (2147483647))。

    思路

    终于有一次写平衡树调试时间不超过 1h 了 /kk。
    一般的二逼平衡树复杂度是 (O(mlog^3 n)) 的,瓶颈在于操作 2 需要二分。
    但是我们知道有一些二分是可以直接放在线段树上的,这样可以去掉一个 (log n)。但是由于操作二并不是一个前缀的询问,所以没有办法放到线段树上。
    但是我们发现,排名为 (k) 是一个前缀且支持二分,所以我们考虑换一种思路,我们用线段树维护 val,平衡树维护 key。
    也就是线段树上区间 ([l,r]) 的平衡树表示的是数值在 ([l,r]) 的位置的下标有哪些。显然一个下标只会被 (O(log n)) 个区间覆盖。所以空间复杂度是 (O(nlog n))
    接下来对于每一个操作:

    1. 直接查询数值在 ([1,k]) 中有多少个下标在 ([l,r])
    2. 在线段树上二分数值,每次查询左子树有多少个下标在 ([l,r]) 内,选择往左还是往右。
    3. 将所有包含这个下标的数值区间所对应平衡树内删掉这个下标,然后插入新的下标。为了省空间,我写了一个垃圾回收。
    4. 先查询 (k-1) 在区间内的排名 (rk),然后查询排名为 (rk) 的数值。
    5. 先查询 (k) 在区间内的排名 (rk),然后查询排名为 (rk+1) 的数值。

    注意询问之前要把所有包含数值的操作离散化。
    时空复杂度均为 (O(mlog^2 n))

    代码

    4.5Kb。比我想象的长一些。

    // I LOVE DATA STRUCTURE!!!
    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=100010,LG=18,Inf=2147483647;
    int n,m,cnt,orz,a[N],b[N];
    
    struct Query
    {
    	int l,r,opt,k;
    }ask[N];
    
    struct Treap
    {
    	int lc[N*LG],rc[N*LG],val[N*LG],size[N*LG],dat[N*LG];
    	queue<int> q;
    	
    	Treap()
    	{
    		while (q.size()) q.pop();
    		for (int i=1;i<N*LG;i++) q.push(i);
    	}
    	
    	int New(int v)
    	{
    		int x=q.front(); q.pop();
    		lc[x]=rc[x]=0; size[x]=1;
    		val[x]=v; dat[x]=rand();
    		return x;
    	}
    	
    	void pushup(int x)
    	{
    		size[x]=size[lc[x]]+size[rc[x]]+1;
    	}
    	
    	void zig(int &x)
    	{
    		int y=lc[x],z=rc[y];
    		rc[y]=x; lc[x]=z; x=y;
    		pushup(rc[x]); pushup(x);
    	}
    	
    	void zag(int &x)
    	{
    		int y=rc[x],z=lc[y];
    		lc[y]=x; rc[x]=z; x=y;
    		pushup(lc[x]); pushup(x);
    	}
    	
    	int build()
    	{
    		int x=New(Inf),y=New(-Inf);
    		lc[x]=y; size[x]=2;
    		return x;
    	}
    	
    	int ins(int x,int v)
    	{
    		if (!x) x=New(v);
    		else if (val[x]>v)
    		{
    			lc[x]=ins(lc[x],v);
    			if (dat[lc[x]]>dat[x]) zig(x);
    		}
    		else
    		{
    			rc[x]=ins(rc[x],v);
    			if (dat[rc[x]]>dat[x]) zag(x);
    		}
    		pushup(x);
    		return x;
    	}
    	
    	int del(int x,int v)
    	{
    		if (val[x]==v)
    		{
    			if (!lc[x] && !rc[x]) { q.push(x); return 0; }
    			if (!rc[x] || (lc[x] && dat[rc[x]]<dat[lc[x]]))
    				zig(x),rc[x]=del(rc[x],v);
    			else
    				zag(x),lc[x]=del(lc[x],v);
    		}
    		else if (val[x]>v)
    		{
    			lc[x]=del(lc[x],v);
    			if (dat[lc[x]]>dat[x]) zig(x);
    		}
    		else
    		{
    			rc[x]=del(rc[x],v);
    			if (dat[rc[x]]>dat[x]) zag(x);
    		}
    		pushup(x);
    		return x;
    	}
    	
    	int getrk(int x,int v)
    	{
    		if (!x) return 0;
    		if (val[x]==v) return size[lc[x]];
    		if (val[x]>v) return getrk(lc[x],v);
    		if (val[x]<v) return size[lc[x]]+1+getrk(rc[x],v);
    		return 23333;
    	}
    	
    	void debug(int x)
    	{
    		if (lc[x]) printf("%d %d
    ",val[x],val[lc[x]]);
    		if (rc[x]) printf("%d %d
    ",val[x],val[rc[x]]);
    		if (lc[x]) debug(lc[x]);
    		if (rc[x]) debug(rc[x]);
    	}
    }treap;
    
    struct SegTree
    {
    	int rt[N*4];
    	
    	void build(int x,int l,int r)
    	{
    		rt[x]=treap.build();
    		if (l==r) return;
    		int mid=(l+r)>>1;
    		build(x*2,l,mid); build(x*2+1,mid+1,r);
    	}
    	
    	void update(int x,int l,int r,int k,int v,bool tag)
    	{
    		if (tag) rt[x]=treap.ins(rt[x],v);
    			else rt[x]=treap.del(rt[x],v);
    		if (l==r) return;
    		int mid=(l+r)>>1;
    		if (k<=mid) update(x*2,l,mid,k,v,tag);
    			else update(x*2+1,mid+1,r,k,v,tag);
    	}
    	
    	int query1(int x,int l,int r,int ql,int qr,int pl,int pr)
    	{
    		if (ql<=l && r<=qr)
    			return treap.getrk(rt[x],pr+1)-treap.getrk(rt[x],pl);
    		int mid=(l+r)>>1;
    		int sum=0;
    		if (ql<=mid) sum+=query1(x*2,l,mid,ql,qr,pl,pr);
    		if (qr>mid) sum+=query1(x*2+1,mid+1,r,ql,qr,pl,pr);
    		return sum;
    	}
    	
    	int query2(int x,int l,int r,int ql,int qr,int k)
    	{
    		if (l==r) return l;
    		int mid=(l+r)>>1;
    		int cnt=treap.getrk(rt[x*2],qr+1)-treap.getrk(rt[x*2],ql);
    		if (k<=cnt) return query2(x*2,l,mid,ql,qr,k);
    			else return query2(x*2+1,mid+1,r,ql,qr,k-cnt);
    	}
    }seg;
    
    int main()
    {
    	srand(53962);
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    		b[++cnt]=a[i];
    	}
    	for (int i=1;i<=m;i++)
    	{
    		scanf("%d",&ask[i].opt);
    		if (ask[i].opt==3)
    		{
    			scanf("%d%d",&ask[i].l,&ask[i].k);
    			b[++cnt]=ask[i].k;
    		}
    		else
    			scanf("%d%d%d",&ask[i].l,&ask[i].r,&ask[i].k);
    	}
    	sort(b+1,b+1+cnt);
    	cnt=unique(b+1,b+1+cnt)-b-1;
    	seg.build(1,1,cnt);
    	for (int i=1;i<=n;i++)
    	{
    		a[i]=lower_bound(b+1,b+1+cnt,a[i])-b;
    		seg.update(1,1,cnt,a[i],i,1);
    	}
    	for (int i=1;i<=m;i++)
    	{
    		if (ask[i].opt==1)
    		{
    			int k=upper_bound(b+1,b+1+cnt,ask[i].k-1)-b-1;
    			printf("%d
    ",seg.query1(1,1,cnt,1,k,ask[i].l,ask[i].r)+1);
    		}
    		if (ask[i].opt==2)
    			printf("%d
    ",b[seg.query2(1,1,cnt,ask[i].l,ask[i].r,ask[i].k)]);
    		if (ask[i].opt==3)
    		{
    			int k=lower_bound(b+1,b+1+cnt,ask[i].k)-b;
    			seg.update(1,1,cnt,a[ask[i].l],ask[i].l,0);
    			seg.update(1,1,cnt,k,ask[i].l,1);
    			a[ask[i].l]=k;
    		}
    		if (ask[i].opt==4)
    		{
    			int k=upper_bound(b+1,b+1+cnt,ask[i].k-1)-b-1;
    			if (k==0) { printf("%d
    ",-Inf); continue; }
    			int rk=seg.query1(1,1,cnt,1,k,ask[i].l,ask[i].r);
    			if (rk==0) { printf("%d
    ",-Inf); continue; }
    			printf("%d
    ",b[seg.query2(1,1,cnt,ask[i].l,ask[i].r,rk)]);
    		}
    		if (ask[i].opt==5)
    		{
    			int k=upper_bound(b+1,b+1+cnt,ask[i].k)-b-1;
    			int rk=seg.query1(1,1,cnt,1,k,ask[i].l,ask[i].r)+1;
    			if (rk>ask[i].r-ask[i].l+1) { printf("%d
    ",Inf); continue; }
    			printf("%d
    ",b[seg.query2(1,1,cnt,ask[i].l,ask[i].r,rk)]);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    Linux下应急工具
    Windows NTLM Hash和Hash传递、Key传递攻击
    渗透中Meterpreter基本操作和对应的windows上的排查或者现象
    捕获一款无名挖矿木马(门罗币)样本分析
    Python守护进程和脚本单例运行
    iOS 支付宝支付
    iOS UI进阶-6.0 手势
    Objective-C中NSArray的基本用法示例
    UIImageView的图片居中问题
    iOS UI基础-19.0 UICollectionView
  • 原文地址:https://www.cnblogs.com/stoorz/p/14226841.html
Copyright © 2020-2023  润新知