JZOJ4605排序

本文介绍了一种使用线段树进行区间排序的优化算法,通过将排序后的序列用线段树表示,实现O(nlogn)的时间复杂度。讨论了如何在区间内进行升序或降序排序,并通过势能分析验证算法的有效性。

我居然写B组题的题解!!!
一个排列,每次操作给一段[l,r]的数升序或降序排序,最后求某一个位置的值
显然我们可以二分然后把数字转成0/1然后用线段树区间赋值。O(nlog2n)O(nlog^2n)O(nlog2n)
然而有一个简单的O(nlogn)O(nlogn)O(nlogn)做法,还可以支持一个log在线查询。
就是将排好序的序列用一个线段树表示,如果要对一段区间排序,就将左右端点所在的线段树裂开,然后将区间内部的所有线段树做线段树合并。时空都是一个log的。
用线段树合并的势能分析,势能函数就是当前的节点的个数,每次合并操作都会减小一个节点,分裂操作最多增加log个节点。
然而跑得跟两个log差不多。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<set>
using namespace std;
typedef long long ll;
typedef pair<int,int> pi;
const int N=2e5+10,M=1e7;
#define x1 c[x][0]
#define x2 c[x][1]
int tr[M],c[M][2],n,m,ty[M],t;
set<pi> s;
typedef set<pi>::iterator it;
void update(int x){tr[x]=tr[x1]+tr[x2];}
pi split(int x,int v){
	if (v==0)return make_pair(0,x);
	if (v==tr[x])return make_pair(x,0);
	int x0=++t;
	if (tr[x1]<=v){
		pi z=split(x2,v-tr[x1]);
		x2=z.first;c[x0][1]=z.second;
	}
	else {
		c[x0][1]=x2;x2=0;
		pi z=split(x1,v);
		x1=z.first;c[x0][0]=z.second;
	}
	update(x);update(x0);
	return make_pair(x,x0);
}
int merge(int x,int y){
	if (!x||!y)return x^y;
	x1=merge(x1,c[y][0]);
	x2=merge(x2,c[y][1]);
	update(x);
	return x;
}
int build(int l,int r,int px){
	int x=++t;
	tr[x]=1;
	if (l==r){return x;}
	int Md=l+r>>1;
	if (px<=Md)x1=build(l,Md,px);
	else x2=build(Md+1,r,px);
	return x;
}
void split(int x){
	pi z=*--s.lower_bound(make_pair(x,1<<30));
	if (z.first==x)return;
	s.erase(z);
	int d=z.second,q=ty[d],h=z.first;
	z=split(d,ty[d]?tr[d]-(x-h):x-h);
	ty[z.first]=ty[z.second]=q;
	if (q)swap(z.first,z.second);
	s.insert(make_pair(h,z.first));
	s.insert(make_pair(x,z.second));
}
int a[N],t1;
void go(int x,int l,int r,int ty){
	if (!x)return;
	if (l==r){a[++t1]=l;return;}
	int Md=l+r>>1;
	if (!ty)go(x1,l,Md,ty),go(x2,Md+1,r,ty);
	else go(x2,Md+1,r,ty),go(x1,l,Md,ty);
}
int main(){
	cin>>n>>m;
	for (int i=1;i<=n;i++){
		int x,z;
		scanf("%d",&x);
		z=build(1,n,x);
		s.insert(make_pair(i,z));
	}
	s.insert(make_pair(n+1,0));
	for (int i=1;i<=m;i++){
		int tp,l,r;
		scanf("%d%d%d",&tp,&l,&r);
		split(l);split(r+1);
		int rt=0,cnt=0;
		it st=s.lower_bound(make_pair(l,0)),ed=s.lower_bound(make_pair(r+1,0));
		static pi pos[N];
		for (it z=st;z!=ed;z++)rt=merge(rt,z->second),pos[++cnt]=*z;
		for (int i=1;i<=cnt;i++)s.erase(pos[i]);
		ty[rt]=tp;
		s.insert(make_pair(l,rt));
	}
	for (auto i:s)go(i.second,1,n,ty[i.second]);
	for (int x;scanf("%d",&x)!=EOF;)
		printf("%d ",a[x]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值