[HEOI2016/TJOI2016]排序 线段树合并+set or 二分+线段树

这题两种主流的做法:

1.线段树合并。复杂度mlogn

2.二分+01线段树mlognlogn

主要讲第一种吧。第一种更快。这里我们要巧妙地运用线段树的合并和分裂,一开始全都是只有一个叶子的n棵线段树,假如l到r需要排序,就把l到r合并了,讲升序和降序记录在左端点l。用set维护树根,一开始set里面有n个树根1~n,以及一个虚根n+1。树set[i]维护的区间set[i]~set[i+1]-1的信息

假如set里面是 1 2 5 6 (n=5,6是虚根)

那么就维护了三棵树1 2 5 分别维护 区间1~1,2~4,5~5的信息

最开始set里面是 1 2 3 4 5 6 分别维护区间1~1,2~2,,,,5~5的信息

假如有个操作是 0 2 4 把2~4升序排序 

我们就暴力把3~4这些树合并到2上面  然后在2位置标记是升序还是降序   并且在set中删除根3 4

就变成了 1 2 5 6 

假如下一个操作是 1 3 4 显然在上一步我们把3~4并到2了  我们要用lower_bound去找到2 然后从2这棵树中把3,4分裂出来,由于2出的标记是升序(上一次操作标记的) 于是我们就把较大的两个数 分出去就行了 反之如果是降序标记 我们把较小的两个数分出去就行

#include<bits/stdc++.h>
using namespace std;
#define R register int
#define mid (l+r>>1)
#define il inline
typedef set<int>::iterator IT;
int n,m;
const int N = 1e5+10,M = 5e6+10;
il int read(){
	int w=0,x=0;char c=0;
	while(c>'9'||c<'0') w|=c=='-',c=getchar();
	while(c<='9'&&c>='0') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return w?-x:x;
}
int ls[M],rs[M],s[M],tot,op[N],rt[N],mem[M],ct;
set<int>srt;
il int newnode(){
	return ct?mem[ct--]:++tot;
}
il void del(R x){
	mem[++ct]=x;s[x]=ls[x]=rs[x]=0;
}
void update(R &x,R l,R r,R pos){
	if(!x) x=newnode();s[x]=1;
	if(l==r) return;
	if(pos<=mid) update(ls[x],l,mid,pos);
	else update(rs[x],mid+1,r,pos);
}
int merge(R x,R y){
	if(!x||!y) return x+y;
	s[x]+=s[y];
	ls[x]=merge(ls[x],ls[y]);
	rs[x]=merge(rs[x],rs[y]);
	del(y);
	return x;
}
int query(R x,R l,R r){
	if(l==r) return l;
	if(ls[x]) return query(ls[x],l,mid);
	else return query(rs[x],mid+1,r);
}
void split(R x,R &y,R k,R op){
	if(s[x]==k) return;
	s[y=newnode()]=s[x]-k,s[x]=k;
	if(op){
		if(k<=s[rs[x]]) split(rs[x],rs[y],k,op),swap(ls[x],ls[y]);
		else split(ls[x],ls[y],k-s[rs[x]],op);
	}else{
		if(k<=s[ls[x]]) split(ls[x],ls[y],k,op),swap(rs[x],rs[y]);
		else split(rs[x],rs[y],k-s[ls[x]],op);
	} 
}
il IT spl(R x){
	IT w = srt.lower_bound(x);
	if(*w==x) return w;
	--w;split(rt[*w],rt[x],x-*w,op[x]=op[*w]);
	return srt.insert(x).first;
}
int main(){
	n=read(),m=read();
	srt.insert(n+1);
	for(R i = 1; i <= n; i++){
		R g;
		g=read();
		update(rt[i],1,n,g);
		srt.insert(i);
	}
	for(R i = 1; i <= m; i++){
		R o,l,r;
		o=read(),l=read(),r=read();
		IT IL=spl(l),IR=spl(r+1);
		for(IT i=++IL;i!=IR;i++) rt[l]=merge(rt[l],rt[*i]);
		op[l]=o;srt.erase(IL,IR);
	}
	R qp;
	qp=read();
	spl(qp),spl(qp+1);
	printf("%d\n",query(rt[qp],1,n));
	return 0;
}

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值