线段树有一种用法,是用多个值域线段树实现一些操作:
1、合并
2、分裂【分出前k小的数
3、查询K小
尝试用最暴力的方法实现合并
暴力实现的复杂度分析:
分裂操作在线段树上的路径和查询K小的路径长度都是log n的,
所以分裂和找k小的复杂度是每次严格log n的,且分裂一次最多增加log n个点
合并实现方法具体如下↓
void merge(root1,root2){//合并到root1上
if 两颗左子树中有空树 直接给root1接上不是空树的左子树
else merge(root1.leftson,root2.leftson)
if 两颗右子树中有空树 直接给root1接上不是空树的右子树
else merge(root1.rightson,root2.rightson)
删掉root2这个点
}
调用一次合并函数的时间是常数,
而合并函数每调用一次就会删掉一个点,
所以合并的总代价为删掉的点数和,这个代价是可以接受的
点数初始时有n log n个,且只有在分裂时才会增加,最多增加m log n个
所以复杂度是总共 O((m+n) log n)
BZOJ 4552 排序 ←这道题可以用这种方法
把原序列看做n个只有一个元素的集合
用n个值域线段树维护这些集合(动态开点)
排序操作就是合并某些集合,
排序操作可能会割掉一些集合的一部分,而那些集合是排好序了的,
所以就是分裂出前k小
最后还要查询一个集合中的第k小数
由于有升降序,得给每个集合多加一个type
要查找合并哪几个集合,只用把集合的坐标信息丢进平衡树维护就能log n查到开头,
然后从左到右一个个地合并就行了
时间复杂度比正解更优,是n log n的,且可以在线支持多个询问和修改操作
后来发现早已有人发明这种用法orz
这篇文章讲的线段树貌似和我弄出来的一样
http://wenku.baidu.com/link?url=Y5MG6RID0tMSc35D4GWKy0yrVwoWJKnnqUO7JCG-Mpb-bKHEBN_TIsyN2AAyqah0kbzpW82sDY4erLYYqSTuH2i9JojCSs1QT_5ZaQTChuW
BZOJ4552 AC code
1、合并
2、分裂【分出前k小的数
3、查询K小
尝试用最暴力的方法实现合并
暴力实现的复杂度分析:
分裂操作在线段树上的路径和查询K小的路径长度都是log n的,
所以分裂和找k小的复杂度是每次严格log n的,且分裂一次最多增加log n个点
合并实现方法具体如下↓
void merge(root1,root2){//合并到root1上
if 两颗左子树中有空树 直接给root1接上不是空树的左子树
else merge(root1.leftson,root2.leftson)
if 两颗右子树中有空树 直接给root1接上不是空树的右子树
else merge(root1.rightson,root2.rightson)
删掉root2这个点
}
调用一次合并函数的时间是常数,
而合并函数每调用一次就会删掉一个点,
所以合并的总代价为删掉的点数和,这个代价是可以接受的
点数初始时有n log n个,且只有在分裂时才会增加,最多增加m log n个
所以复杂度是总共 O((m+n) log n)
BZOJ 4552 排序 ←这道题可以用这种方法
把原序列看做n个只有一个元素的集合
用n个值域线段树维护这些集合(动态开点)
排序操作就是合并某些集合,
排序操作可能会割掉一些集合的一部分,而那些集合是排好序了的,
所以就是分裂出前k小
最后还要查询一个集合中的第k小数
由于有升降序,得给每个集合多加一个type
要查找合并哪几个集合,只用把集合的坐标信息丢进平衡树维护就能log n查到开头,
然后从左到右一个个地合并就行了
时间复杂度比正解更优,是n log n的,且可以在线支持多个询问和修改操作
后来发现早已有人发明这种用法orz
这篇文章讲的线段树貌似和我弄出来的一样
http://wenku.baidu.com/link?url=Y5MG6RID0tMSc35D4GWKy0yrVwoWJKnnqUO7JCG-Mpb-bKHEBN_TIsyN2AAyqah0kbzpW82sDY4erLYYqSTuH2i9JojCSs1QT_5ZaQTChuW
BZOJ4552 AC code
#include<iostream>
#include<queue>
using namespace std;
#define mid ((l+r)>>1)
#define ls t<<1,l,mid
#define rs t<<1|1,mid+1,r
#define ND 2000010
#define mxn 100010
int n,m,k,x,tt,val,i,last,op,L,R,now,nxt,LEFT,RIGHT;
struct data{
int l,r,tp,nxt;
}c[ND];
struct segment_tree{
struct data{
bool exi;
int rmax;
}a[mxn<<2];
void update(int t){
a[t]=(data){
a[t<<1].exi||a[t<<1|1].exi,
a[t<<1|1].rmax?a[t<<1|1].rmax:a[t<<1].rmax
};
}
void ins(int t,int l,int r){
if (l==r) a[t]=(data){k?1:0,k}; else
if (x<=mid) ins(ls),update(t); else
ins(rs),update(t);
}
int ask(int t,int l,int r){
if (l>x||(!a[t].exi)) return 0;
if (l==r) return a[t].rmax;
return (tt=ask(rs))?tt:(mid<=x?a[t<<1].rmax:ask(ls));
}
}T;
queue <int> q;
int newnode(){
int k=q.front();
q.pop();
return k;
}
struct node{
int l,r,sum;
}a[ND];
void clear(int t){
a[t]=a[0];
q.push(t);
}
void build(int t,int l,int r){
a[t].sum=1;
if (l==r) return;
if (val<=mid) build(a[t].l=newnode(),l,mid);
else build(a[t].r=newnode(),mid+1,r);
}
int merge(int t1,int t2){
if (t1==0||t2==0) return t1+t2;
a[t1].l=merge(a[t1].l,a[t2].l);
a[t1].r=merge(a[t1].r,a[t2].r);
a[t1].sum+=a[t2].sum;
clear(t2);
return t1;
}
void split(int t1,int t2,int k){
int tt=a[a[t1].l].sum;
if (k>tt) split(a[t1].r,a[t2].r=newnode(),k-tt); else swap(a[t2].r,a[t1].r);
if (k<tt) split(a[t1].l,a[t2].l=newnode(),k);
a[t2].sum=a[t1].sum-k;
a[t1].sum=k;
}
int ask(int t,int l,int r,int k){
if (l==r) return l;
int tt=a[a[t].l].sum;
if (k>tt) return ask(a[t].r,mid+1,r,k-tt);
else return ask(a[t].l,l,mid,k);
}
int main(){
for (i=1;i<=ND-10;i++)q.push(i);
cin>>n>>m;
for (x=1,last=ND-5;x<=n;last=k,x++){
cin>>val;
c[k=c[last].nxt=newnode()]=(data){x,x,0,0};
T.ins(1,1,n);
build(k,1,n);
}
while (m--){
cin>>op>>L>>R;
x=L;
LEFT=T.ask(1,1,n);
if (L==c[LEFT].l){
RIGHT=newnode();
swap(a[RIGHT],a[LEFT]);
swap(c[RIGHT],c[LEFT]);
c[now=LEFT]=(data){L,R,op,RIGHT};
} else{
c[now=newnode()]=(data){L,R,op,RIGHT=newnode()};
if (!c[LEFT].tp) split(LEFT,RIGHT,L-c[LEFT].l);
else split(LEFT,RIGHT,c[LEFT].r-L+1),swap(a[LEFT],a[RIGHT]);
c[RIGHT]=c[LEFT];
c[RIGHT].l=L;
c[LEFT].r=L-1;
c[LEFT].nxt=now;
}
for (nxt=RIGHT;nxt&&R>=c[nxt].r;last=nxt,nxt=c[nxt].nxt,c[last]=c[0]){
merge(now,nxt);
x=c[nxt].l,k=0;
T.ins(1,1,n);
}
c[now].nxt=nxt;
if (nxt!=0&&c[nxt].l<=R){
x=c[nxt].l,k=0;
T.ins(1,1,n);
if (c[nxt].tp) split(nxt,tt=newnode(),c[nxt].r-R);
else split(nxt,tt=newnode(),R-c[nxt].l+1),swap(a[nxt],a[tt]);
merge(now,tt);
x=c[nxt].l=R+1;
k=nxt;
T.ins(1,1,n);
}
x=L,k=now;
T.ins(1,1,n);
}
cin>>x;
now=T.ask(1,1,n);
if (c[now].tp) cout<<ask(now,1,n,c[now].r-x+1)<<"\n";
else cout<<ask(now,1,n,x-c[now].l+1)<<"\n";
return 0;
}