我居然写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]);
}

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

3506

被折叠的 条评论
为什么被折叠?



