线段树合并与分裂

http://blog.csdn.net/zawedx/article/details/51818475

由于上面这篇文章讲的很清楚了,不打算再讲一遍......骗访问量也要按基本法

利用这种动态开点的值域线段树可以解决一堆有序集合进行合并/分裂/查询k小的问题,最好用的就是在排序问题中。

例1 bzoj4552

m次排序,每次对一个区间升序或降序排序,最后询问一个位置的值。

有一种比较咸鱼的做法是二分答案然后转化为01序列来做,这里就不说了= =

我们可以把排序好的一坨当做一个有序集合,用一个set维护一下这些集合的起止端点,然后排序的时候只要把这些集合全部并在一起就行了。

#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <string>
#include <bitset>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <algorithm>
#include <sstream>
#include <stack>
#include <iomanip>
using namespace std;
#define pb push_back
#define mp make_pair
typedef pair<int,int> pii;
typedef long long ll;
typedef double ld;
typedef vector<int> vi;
#define fi first
#define se second
#define fe first
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define Edg int M=0,fst[SZ],vb[SZ],nxt[SZ];void ad_de(int a,int b){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;}void adde(int a,int b){ad_de(a,b);ad_de(b,a);}
#define Edgc int M=0,fst[SZ],vb[SZ],nxt[SZ],vc[SZ];void ad_de(int a,int b,int c){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;vc[M]=c;}void adde(int a,int b,int c){ad_de(a,b,c);ad_de(b,a,c);}
#define es(x,e) (int e=fst[x];e;e=nxt[e])
#define esb(x,e,b) (int e=fst[x],b=vb[e];e;e=nxt[e],b=vb[e])
#define VIZ {printf("digraph G{\n"); for(int i=1;i<=n;i++) for es(i,e) printf("%d->%d;\n",i,vb[e]); puts("}");}
#define VIZ2 {printf("graph G{\n"); for(int i=1;i<=n;i++) for es(i,e) if(vb[e]>=i)printf("%d--%d;\n",i,vb[e]); puts("}");}
#define SZ 666666
#define SG 9999999
//merge-split seg begin
int ss[SG],sn=0,ch[SG][2],s[SG];
#define er(x) ss[++sn]=x
inline int alc_()
{
    int x=ss[sn--];
    ch[x][0]=ch[x][1]=s[x]=0;
    return x;
}
#define alc alc_()
//make a seg with only node p
void build(int& x,int l,int r,int p)
{
    x=alc; s[x]=1;
    //cout<<"build"<<x<<","<<l<<","<<r<<','<<p<<"\n";
    if(l==r) return;
    int m=(l+r)>>1;
    if(p<=m) build(ch[x][0],l,m,p);
    else build(ch[x][1],m+1,r,p);
}
//merge t1&t2
int merge(int t1,int t2)
{
    if(t1&&t2);else return t1^t2;
    ch[t1][0]=merge(ch[t1][0],ch[t2][0]);
    ch[t1][1]=merge(ch[t1][1],ch[t2][1]);
    s[t1]+=s[t2]; er(t2); return t1;
}
//split t1 to t1&t2 so that s[t1]=k
void split(int t1,int&t2,int k)
{
    t2=alc;
    int ls=s[ch[t1][0]];
    if(k>ls) split(ch[t1][1],ch[t2][1],k-ls);
    else swap(ch[t1][1],ch[t2][1]);
    if(k<ls) split(ch[t1][0],ch[t2][0],k);
    s[t2]=s[t1]-k; s[t1]=k;
}
int ask(int x,int l,int r,int k)
{
    if(l==r) return l;
    int ls=s[ch[x][0]];
    int m=(l+r)>>1;
    if(k>ls) return ask(ch[x][1],m+1,r,k-ls);
    return ask(ch[x][0],l,m,k);
}
void ps(int x,int l,int r,int dep=0)
{
    if(!x) return;
    for(int i=1;i<=dep;i++) putchar(' ');
    cout<<x<<" "<<s[x]<<" "<<l<<","<<r<<" lc"<<ch[x][0]<<"rc"<<ch[x][1]<<"\n";
    ps(ch[x][0],l,(l+r)>>1,dep+1);
    ps(ch[x][1],(l+r)/2+1,r,dep+1);
}
//seg end
bool typ[SZ]; //0<  1>
int rots[SZ],rs[SZ]; //i pos store l=i
set<int> sol;
int n,q,a[SZ];
//split x so that rs[x]=s
void sp(int x,int s)
{
    //cout<<"split"<<x<<","<<s<<"\n";
    if(s>=rs[x]||s<x) return;
    if(!typ[x]) split(rots[x],rots[s+1],s-x+1);
    else
    {
        rots[s+1]=rots[x];
        split(rots[s+1],rots[x],rs[x]-s);
    }
    rs[s+1]=rs[x]; rs[x]=s;
    sol.insert(s+1); typ[s+1]=typ[x];
}
//it's your task to edit typ[a]!
void mg(int a,int b)
{
    //cout<<"mg"<<a<<","<<b<<"\n";
    if(a==b) return;
    sol.erase(b);
    rots[a]=merge(rots[a],rots[b]);
    rs[a]=rs[b];
    //cout<<a<<","<<rs[a]<<"\n";
}
int qpos(int x,int k)
{
    k-=x-1;
    if(!typ[x]) return ask(rots[x],1,n,k);
    else return ask(rots[x],1,n,rs[x]-x+2-k);
}
void dbg()
{
    cout<<sol.size()<<"\n";
    for(set<int>::iterator g=sol.begin();g!=sol.end();g++)
    {
        cout<<*g<<" "<<rs[*g]<<" "<<typ[*g]<<"\n";
        ps(rots[*g],1,n);
    }
}
#define ch ____ch
char ch,B[1<<15],*S=B,*T=B;
#define getc() (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++)
#define isd(c) (c>='0'&&c<='9')
int aa,bb;int F(){
    while(ch=getc(),!isd(ch)&&ch!='-');ch=='-'?aa=bb=0:(aa=ch-'0',bb=1);
    while(ch=getc(),isd(ch))aa=aa*10+ch-'0';return bb?aa:-aa;
}
#define gi F() 
int main()
{
    for(int i=SG-1;i>=1;i--) ss[++sn]=i;
    //scanf("%d%d",&n,&q);
    n=gi, q=gi;
    for(int i=1;i<=n;i++)
    {
        a[i]=gi;
        //scanf("%d",a+i);
        build(rots[i],1,n,a[i]);
        sol.insert(i); rs[i]=i;
    }
    static int tmp[SZ],tn=0;
    while(q--)
    {
        int o=gi,l=gi,r=gi;
        //scanf("%d%d%d",&o,&l,&r);
        {
        set<int>::iterator w=sol.upper_bound(l); sp(*(--w),l-1);
        w=sol.upper_bound(r); sp(*(--w),r);
        }
        set<int>::iterator p1=sol.lower_bound(l),
        p2=sol.upper_bound(r); --p2;
        int tg=*p1;
        if(p1!=p2)
        {
        for(set<int>::iterator i=++p1;;++i)
        {
            tmp[++tn]=*i;
            if(i==p2) break;
        }
        for(int i=1;i<=tn;i++) mg(tg,tmp[i]); tn=0;
        }
        typ[tg]=o;
    }
    int r=gi;
    set<int>::iterator w=sol.upper_bound(r);
    int x=*(--w); printf("%d\n",qpos(x,r));
}

例2 yzoj2753

问题可以被当做是把序列中的一段进行升序/降序排序或者交换两个元素。

同样可以咸鱼一点二分答案之后做,不过这也不是重点...

做法和上一题差不多。

#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <string>
#include <bitset>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <algorithm>
#include <sstream>
#include <stack>
#include <iomanip>
using namespace std;
#define pb push_back
#define mp make_pair
typedef pair<int,int> pii;
typedef long long ll;
typedef double ld;
typedef vector<int> vi;
#define fi first
#define se second
#define fe first
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define Edg int M=0,fst[SZ],vb[SZ],nxt[SZ];void ad_de(int a,int b){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;}void adde(int a,int b){ad_de(a,b);ad_de(b,a);}
#define Edgc int M=0,fst[SZ],vb[SZ],nxt[SZ],vc[SZ];void ad_de(int a,int b,int c){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;vc[M]=c;}void adde(int a,int b,int c){ad_de(a,b,c);ad_de(b,a,c);}
#define es(x,e) (int e=fst[x];e;e=nxt[e])
#define esb(x,e,b) (int e=fst[x],b=vb[e];e;e=nxt[e],b=vb[e])
#define VIZ {printf("digraph G{\n"); for(int i=1;i<=n;i++) for es(i,e) printf("%d->%d;\n",i,vb[e]); puts("}");}
#define VIZ2 {printf("graph G{\n"); for(int i=1;i<=n;i++) for es(i,e) if(vb[e]>=i)printf("%d--%d;\n",i,vb[e]); puts("}");}
#define SZ 666666
#define SG 7777777
//merge-split seg begin
int ss[SG],sn=0,ch[SG][2],s[SG];
#define er(x) ss[++sn]=x
inline int alc_()
{
    int x=ss[sn--];
    ch[x][0]=ch[x][1]=0;//s[x]=0;
    return x;
}
#define alc alc_()
//make a seg with only node p
void build(int& x,int l,int r,int p)
{
    x=alc; s[x]=1;
    if(l==r) return;
    int m=(l+r)>>1;
    if(p<=m) build(ch[x][0],l,m,p);
    else build(ch[x][1],m+1,r,p);
}
//merge t1&t2
int merge(int t1,int t2)
{
    if(t1&&t2);else return t1^t2;
    ch[t1][0]=merge(ch[t1][0],ch[t2][0]);
    ch[t1][1]=merge(ch[t1][1],ch[t2][1]);
    s[t1]+=s[t2]; er(t2); return t1;
}
//split t1 to t1&t2 so that s[t1]=k
void split(int t1,int&t2,int k)
{
    t2=alc;
    if(ch[t1][0]||ch[t1][1])
    {
        int ls=s[ch[t1][0]];
        if(k>ls) split(ch[t1][1],ch[t2][1],k-ls);
        else swap(ch[t1][1],ch[t2][1]);
        if(k<ls) split(ch[t1][0],ch[t2][0],k);
    }
    s[t2]=s[t1]-k; s[t1]=k;
}
int ask(int x,int l,int r,int k)
{
    if(l==r) return l;
    int ls=s[ch[x][0]];
    int m=(l+r)>>1;
    if(k>ls) return ask(ch[x][1],m+1,r,k-ls);
    return ask(ch[x][0],l,m,k);
}
//seg end
typedef set<int> Set;
int R=1e9;
Set sol;
bool typ[SZ]; //0<  1>
int rots[SZ],rs[SZ]; //i pos store l=i
int n,q,a[SZ];
//split x so that rs[x]=s
void sp(int x,int s)
{
    if(s>=rs[x]||s<x) return;
    if(!typ[x]) split(rots[x],rots[s+1],s-x+1);
    else
    {
        rots[s+1]=rots[x];
        split(rots[s+1],rots[x],rs[x]-s);
    }
    rs[s+1]=rs[x]; rs[x]=s;
    sol.insert(s+1); typ[s+1]=typ[x];
}
//it's your task to edit typ[a]!
void mg(int a,int b)
{
    if(a==b) return;
    sol.erase(b);
    rots[a]=merge(rots[a],rots[b]);
    rs[a]=rs[b];
}
int qpos(int x,int k)
{
    k-=x-1;
    if(!typ[x]) return ask(rots[x],0,R,k);
    else return ask(rots[x],0,R,rs[x]-x+2-k);
}
#define fpos my_fpos
int fpos(int x)
{
    Set::iterator w=sol.upper_bound(x);
    return *(--w);
}
#define ch reader_ch
char ch,B[1<<15],*S=B,*T=B;
#define getc() (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++)
#define isd(c) (c>='0'&&c<='9')
int aa,bb;int F(){
    while(ch=getc(),!isd(ch)&&ch!='-');ch=='-'?aa=bb=0:(aa=ch-'0',bb=1);
    while(ch=getc(),isd(ch))aa=aa*10+ch-'0';return bb?aa:-aa;
}
#define gi F()
void sort(int l,int r,int o)
{
    if(l==r) return;
    static int tmp[SZ],tn=0;
    sp(fpos(l),l-1); sp(fpos(r),r);
    Set::iterator p1=sol.lower_bound(l),
    p2=sol.upper_bound(r); --p2;
    int tg=*p1;
    if(p1!=p2)
    {
        for(Set::iterator i=++p1;;++i)
        {
            tmp[++tn]=*i;
            if(i==p2) break;
        }
        for(int i=1;i<=tn;i++) mg(tg,tmp[i]); tn=0;
    }
    typ[tg]=o;
}
void qwq()
{
    sol.clear();
    sn=R=0;
    for(int i=SG-1;i>=1;i--) ss[++sn]=i;
    n=gi, q=gi;
    for(int i=1;i<=n+n;i++) R=max(R,a[i]=gi);
    for(int i=1;i<=n+n;i++)
    {
        build(rots[i],0,R,a[i]);
        sol.insert(i); rs[i]=i;
    }
    while(q--)
    {
        int o=gi,a=gi,b=gi,c=gi,d=gi;
        if(o==1)
        {
            int p=a*n+b,q=a*n+c;
            sort(p,q,d);
        }
        else
        {
            int p=a*n+b,q=c*n+d;
            sp(fpos(p),p-1);
            sp(fpos(p),p);
            sp(fpos(q),q-1);
            sp(fpos(q),q);
            swap(rots[p],rots[q]);
        }
    }
    int x_=gi,y_=gi,x=x_*n+y_;
    printf("%d\n",qpos(fpos(x),x));
}
int main()
{
    int T=gi;
    while(T--) qwq();
}

于是好好的安利文就成了贴板子= =如果有比较有趣的题再更新吧

另外不是很懂splay和treap要怎么搞这个东西啊

posted @ 2016-12-14 22:46  fjzzq2002  阅读(5836)  评论(0编辑  收藏  举报