线段树的特殊运用

线段树有一种用法,是用多个值域线段树实现一些操作:
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;  
}   

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值