线段树分裂&合并入门题。
对于每个单调段用一个权值线段树维护。一次操作相当于先对 l , r l,r l,r 所在的单调段的权值线段树分裂,然后再合并若干棵的权值线段树。
线段树分裂和 fhq-treap 的分裂类似,只不过根到分裂成的两部分的 l c a lca lca 的那一段路径要复制一遍。
线段树分裂&合并的时间复杂度分析:分裂一定单次是 O ( log n ) O(\log n) O(logn) 的,现在考虑合并的时间复杂度。一开始有 O ( n log n ) O(n\log n) O(nlogn) 个节点,分裂的过程中可能多产生出 O ( m log n ) O(m\log n) O(mlogn) 个节点,而线段树合并的时间复杂度是和总点数成线性的,所以总时间复杂度为 O ( ( n + m ) log n ) O((n+m)\log n) O((n+m)logn)。
线段树合并的时间复杂度和总点数成线性的证明:假设总点数为 N N N,考虑线段树合并过程中进入递归当且仅当两棵树在这个位置有重合节点,而合并完之后两个重合的节点会变为一个节点。也就是说,线段树合并过程中我们每产生 O ( 1 ) O(1) O(1) 的时间复杂度,线段树总点数就会减少 1 1 1。而线段树总点数总是大于 0 0 0 的,所以线段树合并的时间复杂度和总点数成线性。
#include<bits/stdc++.h>
#define N 100010
#define NN 10000010
#define lc(u) ch[u][0]
#define rc(u) ch[u][1]
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
struct data
{
int l,r,rt;
bool tag;
data(){};
data(int _l,int _r,int _rt,bool _tag){l=_l,r=_r,rt=_rt,tag=_tag;}
bool operator < (data b) const {return l<b.l;}
};
int n,m;
int node,ch[NN][2],size[NN];
set<data>s;
void up(int u)
{
size[u]=size[lc(u)]+size[rc(u)];
}
void build(int &u,int l,int r,int x)
{
u=++node;
if(l==r)
{
size[u]=1;
return;
}
int mid=(l+r)>>1;
if(x<=mid) build(lc(u),l,mid,x);
else build(rc(u),mid+1,r,x);
up(u);
}
void split(int &x,int &y,int u,int l,int r,int sz)
{
if(!sz)
{
x=0,y=u;
return;
}
if(sz==size[u])
{
x=u,y=0;
return;
}
x=++node,y=++node;
int mid=(l+r)>>1;
if(sz<=size[lc(u)])
{
split(lc(x),lc(y),lc(u),l,mid,sz);
rc(x)=0,rc(y)=rc(u);
}
else
{
split(rc(x),rc(y),rc(u),mid+1,r,sz-size[lc(u)]);
lc(x)=lc(u),lc(y)=0;
}
up(x),up(y);
}
void split1(int x)
{
if(!x) return;
auto it=s.upper_bound(data(x,0,0,0));it--;
data now=(*it);
if(x==now.r) return;
int lrt,rrt;
if(!now.tag) split(lrt,rrt,now.rt,1,n,x-now.l+1);
else split(rrt,lrt,now.rt,1,n,now.r-x);
s.erase(it);
s.insert(data(now.l,x,lrt,now.tag));
s.insert(data(x+1,now.r,rrt,now.tag));
}
void merge(int &x,int y,int l,int r)
{
if(!x||!y)
{
x=x+y;
return;
}
if(l==r) assert(0);
int mid=(l+r)>>1;
merge(lc(x),lc(y),l,mid);
merge(rc(x),rc(y),mid+1,r);
up(x);
}
void work(bool tag,int l,int r)
{
split1(l-1),split1(r);
auto it=s.lower_bound(data(l,0,0,0)),sit=it;
int rt=(*sit).rt;
for(it++;it!=s.end()&&(*it).r<=r;it++)
merge(rt,(*it).rt,1,n);
s.erase(sit,it);
s.insert(data(l,r,rt,tag));
}
int query(int u,int l,int r,int sz)
{
if(l==r) return l;
int mid=(l+r)>>1;
if(sz<=size[lc(u)]) return query(lc(u),l,mid,sz);
return query(rc(u),mid+1,r,sz-size[lc(u)]);
}
int query1(int p)
{
auto it=s.upper_bound(data(p,0,0,0));it--;
data now=(*it);
if(now.tag) return query(now.rt,1,n,now.r-p+1);
return query(now.rt,1,n,p-now.l+1);
}
int main()
{
n=read(),m=read();
for(int i=1;i<=n;i++)
{
int x=read(),nrt;
build(nrt,1,n,x);
s.insert(data(i,i,nrt,0));
}
while(m--)
{
bool opt=read();
int l=read(),r=read();
work(opt,l,r);
}
printf("%d\n",query1(read()));
return 0;
}