[HEOI2016/TJOI2016]排序——(在线做法)启发式合并、平衡树、(非玄学)优化

[HEOI2016/TJOI2016]排序

前言

看见这题后立马用 f h q   t r e a p \rm fhq\,treap fhqtreap 搞了一个启发式合并,以为是对的,结果发现复杂度假了。

然后我不禁思索:为什么线段树合并就是对的呢?本质上区别不大啊?

于是探究了一番后,给启发式合并加了个优化就过了。

题解

对于排好序的区间,我们可以合并为一个集合,然后给集合标记升降序。由于区间和集合的数都是有序的,所以我们可以短时间内通过使集合前后分裂把一个有序区间分裂成两个有序区间。这样一来,每次排序操作最多分裂两个有序区间,然后把中间的所有有序区间合并为一个新的有序大区间。

这时我们使用易于分裂的 f h q   t r e a p \rm fhq\,treap fhqtreap 维护集合,启发式合并来完成操作。单看合并的话,貌似均摊是 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n) 的,但是每次分裂时,可能会分裂出很大的区间,导致下次合并非常费力。分析一下势能,一次 O ( log ⁡ n ) O(\log n) O(logn) 的分裂,可能带来 O ( n ) O(n) O(n) 次的合并复杂度,而分裂次数是 O ( m ) O(m) O(m) 级的,所以会T。大的数据远远跑不进时限。

正解是线段树合并,再加上模仿 f h q   t r e a p \rm fhq\,treap fhqtreap 分裂动作的线段树分裂。神奇的是,线段树合并的复杂度却是对的,而且是 O ( n log ⁡ n ) O(n\log n) O(nlogn)线段树分裂好麻烦,不行,咱就用Treap

想想对比线段树合并,我们的启发式合并差在了哪里。线段树合并并不是把要加入的点一个一个地插入,而是把整的区间不断二分成小区间,如果小区间内没有其它妨碍的点就可以直接插入整个区间。

f h q   t r e a p \rm fhq\,treap fhqtreap 是棵平衡的二叉搜索树,所以它的结构其实和线段树非常相似。我们可以考虑多维护一下平衡树上区间的端点,然后就可以模仿“分成小区间插入”的方法优化启发式合并了。

具体地,我们不慌把一棵平衡树拆成散点,而是先保留整棵树,把树根入栈。设被插入的平衡树为 a a a,待插入的平衡树为 b b b,那么以树 b b b 的根的值为参数分裂树 a a a
在这里插入图片描述
当满足 r a 1 < l b ≤ r b < l a 2 r_{a_1}<l_b\le r_b<l_{a_2} ra1<lbrb<la2 时,可以直接把整颗树 b b b 插在中间进行合并操作。如果不满足上面的条件,那么只能先把树根 b b b 单独插入,然后把 b b b 的左右子树入栈。

这样一来,我们就不再是一个点一个点地插入,而是把平衡树分成了许多区间插入。期望情况下,它的插入次数和线段树合并是一样的,均摊一次 O ( log ⁡ n ) O(\log n) O(logn),但是单次插入是 O ( log ⁡ n ) O(\log n) O(logn),所以总复杂度期望 O ( m log ⁡ 2 n ) O(m\log^2n) O(mlog2n)

当然, f h q   t r e a p \rm fhq\,treap fhqtreap 结构并不完全和线段树一样,插入的小区间数也可能比较大,但是期望情况下复杂度确实是对的。由于用了随机数确保平衡,根本无法被卡呗

代码

#include<cstdio>//JZM yyds!!
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define ll long long
#define uns unsigned
#define MAXN 200005
#define INF 1e17
#define lowbit(x) ((x)&(-(x)))
#define IF (it->first)
#define IS (it->second)
using namespace std;
inline ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
	return f?x:-x;
}
struct node{
	int x,y;node(){}
	node(int X,int Y){x=X,y=Y;}
};
struct fhq{
	int ls,rs,a,l,r,siz,val;fhq(){ls=rs=a=siz=0;}
	fhq(int A){ls=rs=0,l=r=a=A,siz=1,val=rand()%MAXN;}
}t[MAXN];
int root,IN;
inline void update(int x){
	t[x].siz=t[t[x].ls].siz+t[t[x].rs].siz+1;
	t[x].l=t[x].r=t[x].a;
	if(t[x].ls)t[x].l=t[t[x].ls].l;
	if(t[x].rs)t[x].r=t[t[x].rs].r;
}
inline node split(int x,int k){
	if(!x)return node(0,0);node res;
	if(t[x].a<=k)res=split(t[x].rs,k),t[x].rs=res.x,update(x),res.x=x;
	else res=split(t[x].ls,k),t[x].ls=res.y,update(x),res.y=x;
	return res;
}
inline node split_(int x,int k){
	if(!x||!k)return node(0,x);node res;
	if(t[t[x].ls].siz>=k)res=split_(t[x].ls,k),t[x].ls=res.y,update(x),res.y=x;
	else res=split_(t[x].rs,k-t[t[x].ls].siz-1),t[x].rs=res.x,update(x),res.x=x;
	return res;
}
inline int mergg(int x,int y){
	if(!x||!y)return x|y;int res;
	if(t[x].val<t[y].val)t[x].rs=mergg(t[x].rs,y),update(x),res=x;
	else t[y].ls=mergg(x,t[y].ls),update(y),res=y;
	return res;
}
struct itn{
	int l,r,rt;
	bool z;itn(){}
	itn(int L,int R,int RT,bool Z){
		l=L,r=R,rt=RT,z=Z;
	}
	bool operator<(const itn&b)const{
		if(l!=b.l)return l<b.l;
		else return r<b.r;
	}
};
set<itn>st;
set<itn>::iterator it;
inline void spl(int mid){
	it=st.lower_bound(itn(mid+1,mid+1,0,0));
	if(it==st.begin())return;it--;
	itn a=*it,b,c;
	int l=a.l,r=a.r;
	st.erase(it);
	if(a.z){
		node x=split_(a.rt,r-mid);
		b=itn(l,mid,x.y,1),c=itn(mid+1,r,x.x,1);
		if(b.rt)st.insert(b);
		if(c.rt)st.insert(c);
	}else{
		node x=split_(a.rt,mid-l+1);
		b=itn(l,mid,x.x,0),c=itn(mid+1,r,x.y,0);
		if(b.rt)st.insert(b);
		if(c.rt)st.insert(c);
	}
}
inline int sch(int mid){
	it=st.lower_bound(itn(mid+1,mid+1,0,0));
	it--;itn a=*it;
	int l=a.l,r=a.r,res;
	if(a.z){
		node x=split_(a.rt,r-mid),y=split_(x.y,1);
		res=t[y.x].a,a.rt=mergg(x.x,mergg(y.x,y.y));
	}else{
		node x=split_(a.rt,mid-l),y=split_(x.y,1);
		res=t[y.x].a,a.rt=mergg(x.x,mergg(y.x,y.y));
	}return res;
}
int del[MAXN],le,len;
itn de[MAXN];
inline int merg(int x,int y){
	if(!x||!y)return x^y;
	if(t[x].r<t[y].l)return mergg(x,y);
	if(t[y].r<t[x].l)return mergg(y,x);
	if(t[x].siz<t[y].siz)swap(x,y);
	le=0,del[++le]=y;
	while(le){
		int in=del[le];le--;
		node u=split(x,t[in].a);
		if(t[u.x].r<t[in].l&&t[u.y].l>t[in].r)
			x=mergg(u.x,mergg(in,u.y));
		else{
			if(t[in].ls)del[++le]=t[in].ls;
			if(t[in].rs)del[++le]=t[in].rs;
			t[in]=fhq(t[in].a);
			x=mergg(u.x,mergg(in,u.y));
		}
	}
	return x;
}
int n,m;
signed main()
{
	srand(time(0));
	n=read(),m=read();
	t[0].r=0,t[0].l=n+1;
	for(int i=1;i<=n;i++)
		t[++IN]=fhq(read()),st.insert(itn(i,i,IN,0));
	for(int i=1;i<=m;i++){
		int op=read(),l=read(),r=read();
		spl(l-1),spl(r);
		it=st.lower_bound(itn(l,l,0,0));
		int rt=0;len=0;
		while(it!=st.end()&&(*it).r<=r)
			rt=merg(rt,(*it).rt),de[++len]=*it,it++;
		while(len)st.erase(de[len]),len--;
		st.insert(itn(l,r,rt,op));
	}
	printf("%d\n",sch(read()));
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值