平衡树(FHQ-Treap/无旋Treap)
更新日志
2025/02/27:更新区间修改,附增文艺平衡树代码。2025/04/27:重构,更新模板。
概念
FHQ-Treap 是一种平衡树。
我认为它的本质作用是动态维护一个数列,一个常用作用是维护一个多重集。
实现
Treap
Treap 是 Tree 和 Heap 的结合体,它同时满足堆和二叉搜索树的性质。
更具体地,每一个节点都有两个属性,一个满足堆,一个满足二叉搜索树。
这个定义使得 Treap 可以维护多重集,但 FHQ-Treap 与其并不完全相同。
FHQ-Treap
FHQ-Treap 其实并不需要满足二叉搜索树的性质。
它维护了一棵树,每个节点对应原数列的一个元素,中序遍历这颗树就可以得到原序列。
当我们给每个节点随机赋值一个权值,使得整棵树的权值满足堆性质(大根堆小根堆随意),那么整棵树的树高将会维持在大约 \(O(\log n)\) 级别。
这使得我们可以在这颗树上搞很多暴力操作。
操作
核心操作
这两个操作是 FHQ-Treap 的最核心的操作,其余所有操作都围绕它们展开。
本质上来说,只有这两个操作会改变树的形态。
一个体现是节点的 update 以及 pushdown 只需要在这两个函数里加就行了。
Merge
合并两棵 Treap,使得新树对应序列就是第二棵树的序列拼接在第一棵树的序列后面。
也就是维护中序遍历顺序,同时维护堆序。
void merge(int a,int b,int &rt){
	if(!a||!b)return rt=a+b,void();
	if(ns[a].pri>ns[b].pri){
		merge(ns[a].rson,b,ns[a].rson);
		update(a);rt=a;
	}else{
		merge(a,ns[b].lson,ns[b].lson);
		update(b);rt=b;
	}
}
Split
把一棵树分裂成两个部分,前一部分包括了原树的前 \(k\) 个元素。
类似于二分,找到那个分界的位置,同时一刀切。因此你需要更新沿途节点的左右子节点
void split(int x,int k,int &rta,int &rtb){
	if(!x)return rta=rtb=0,void();
	if(k>ns[ns[x].lson].siz)rta=x,split(ns[x].rson,k-ns[ns[x].lson].siz-1,ns[x].rson,rtb);
	else split(ns[x].lson,k,rta,ns[x].lson),rtb=x;
	update(x);
}
基本操作
Getrnk
给出一个节点,返回这个节点在原树中的编号,也就是序列中的第几个元素。
这个函数其实不常见,并且有一定的局限性。如果你不需要它的话,那么你也就无需维护父节点编号。但事实上,如果你需要动态维护区间,这个函数还是很有用的,故而还是加上。
可惜的是,可持久化平衡树下,你无法维护父节点。
一路往上走,如果是父节点的右节点,就加上父节点的左子树大小及其本身。
注意,如果你的区间操作涉及左右子树互换,那么这个函数中需要先把到祖先一路的标记都 pushdown 下来。
int getrnk(int idx,int rt){
	int res=ns[ns[idx].lson].siz+1;
	for(;idx!=rt;idx=ns[idx].fa)res+=(idx==ns[ns[idx].fa].rson?ns[ns[ns[idx].fa].lson].siz+1:0);
	return res;
}
Getidx
找到第 \(rnk\) 个节点的编号。二分即可。
int getidx(int rnk,int rt){
	if(ns[ns[rt].lson].siz==rnk-1)return rt;
	if(ns[ns[rt].lson].siz>=rnk)return getidx(rnk,ns[rt].lson);
	else return getidx(rnk-ns[ns[rt].lson].siz-1,ns[rt].rson);
}
二叉搜索树操作
维护可重集只是 FHQ-Treap 的一个功能,因此把这部分函数单独放一块讲。
除了 remove 函数以外,调用其他函数时必须满足这颗树是二叉搜索树。
Getrnk_v
获取所有值小于要求 \(val\) 的元素数量 \(+1\)。
搜索就行。
int getrnk_v(ll val,int rt){
	if(!rt)return 1;
	if(ns[rt].val<val)return ns[ns[rt].lson].siz+1+getrnk_v(val,ns[rt].rson);
	else return getrnk_v(val,ns[rt].lson);
}
Getidx_v
找到第一个大于等于要求 \(val\) 的结点编号。
搜索就行。
int getidx_v(ll val,int rt){
	if(!rt)return 0;
	if(ns[rt].val<val)return getidx_v(val,ns[rt].rson);
	int res=getidx_v(val,ns[rt].lson);
	if(!res)res=rt;
	return res;
}
Insert
插入一个指定节点到指定树。
考虑到把原树分成两部分,左边全部小于等于新节点,右边同理,然后把三者合并即可。
void insert(int idx,int &rt){
	int a,b;
	split(rt,getrnk_v(ns[idx].val,rt)-1,a,b);
	merge(a,idx,a),merge(a,b,rt);
}
remove
从指定树中移除一个指定节点。
void remove(int idx,int &rt){
	int a,b,c;
	split(rt,getrnk(idx,rt)-1,a,b),split(b,1,b,c);
	merge(a,c,rt);
}
区间修改懒标记
在所有涉及子树形态变换的函数以及查询函数递归下一层(最好在判断子树信息语句之前)下传标记即可。和线段树差不多。
注意 getrnk 函数需要预先从上到下把一路的标记下传。
模板
序列维护精简版
auto rnd=mt19937_64(time(0));
struct fhqtreap{
	struct node{
		int lson,rson,fa;
		int siz;
		ll pri;
		ll val;
		node(){siz=1,pri=rnd(),val=0;}
	}ns[N];
	fhqtreap(){ns[0].siz=0;}
	void update(int x){
		ns[x].fa=0;
		ns[x].siz=ns[ns[x].lson].siz+1+ns[ns[x].rson].siz;
		ns[ns[x].lson].fa=ns[ns[x].rson].fa=x;
	}
	void split(int x,int k,int &rta,int &rtb){
		if(!x)return rta=rtb=0,void();
		if(k>ns[ns[x].lson].siz)rta=x,split(ns[x].rson,k-ns[ns[x].lson].siz-1,ns[x].rson,rtb);
		else split(ns[x].lson,k,rta,ns[x].lson),rtb=x;
		update(x);
	}
	void merge(int a,int b,int &rt){
		if(!a||!b)return rt=a+b,void();
		if(ns[a].pri>ns[b].pri){
			merge(ns[a].rson,b,ns[a].rson);
			update(a);rt=a;
		}else{
			merge(a,ns[b].lson,ns[b].lson);
			update(b);rt=b;
		}
	}
	int getrnk(int idx,int rt){
		int res=ns[ns[idx].lson].siz+1;
		for(;idx!=rt;idx=ns[idx].fa)res+=(idx==ns[ns[idx].fa].rson?ns[ns[ns[idx].fa].lson].siz+1:0);
		return res;
	}
	int getidx(int rnk,int rt){
		if(ns[ns[rt].lson].siz==rnk-1)return rt;
		if(ns[ns[rt].lson].siz>=rnk)return getidx(rnk,ns[rt].lson);
		else return getidx(rnk-ns[ns[rt].lson].siz-1,ns[rt].rson);
	}
	int getrnk_v(ll val,int rt){
		if(!rt)return 1;
		if(ns[rt].val<val)return ns[ns[rt].lson].siz+1+getrnk_v(val,ns[rt].rson);
		else return getrnk_v(val,ns[rt].lson);
	}
}fhq;
二叉搜索树封装版
auto rnd=mt19937_64(time(0));
struct fhqtreap{
	struct node{
		int lson,rson,fa;
		int siz;
		ll pri;
		ll val;
		node(){siz=1,pri=rnd(),val=0;}
	}ns[N];
	fhqtreap(){ns[0].siz=0;}
	void update(int x){
		ns[x].fa=0;
		ns[x].siz=ns[ns[x].lson].siz+1+ns[ns[x].rson].siz;
		ns[ns[x].lson].fa=ns[ns[x].rson].fa=x;
	}
	void split(int x,int k,int &rta,int &rtb){
		if(!x)return rta=rtb=0,void();
		if(k>ns[ns[x].lson].siz)rta=x,split(ns[x].rson,k-ns[ns[x].lson].siz-1,ns[x].rson,rtb);
		else split(ns[x].lson,k,rta,ns[x].lson),rtb=x;
		update(x);
	}
	void merge(int a,int b,int &rt){
		if(!a||!b)return rt=a+b,void();
		if(ns[a].pri>ns[b].pri){
			merge(ns[a].rson,b,ns[a].rson);
			update(a);rt=a;
		}else{
			merge(a,ns[b].lson,ns[b].lson);
			update(b);rt=b;
		}
	}
	int getrnk(int idx,int rt){
		int res=ns[ns[idx].lson].siz+1;
		for(;idx!=rt;idx=ns[idx].fa)res+=(idx==ns[ns[idx].fa].rson?ns[ns[ns[idx].fa].lson].siz+1:0);
		return res;
	}
	int getidx(int rnk,int rt){
		if(ns[ns[rt].lson].siz==rnk-1)return rt;
		if(ns[ns[rt].lson].siz>=rnk)return getidx(rnk,ns[rt].lson);
		else return getidx(rnk-ns[ns[rt].lson].siz-1,ns[rt].rson);
	}
	int getrnk_v(ll val,int rt){
		if(!rt)return 1;
		if(ns[rt].val<val)return ns[ns[rt].lson].siz+1+getrnk_v(val,ns[rt].rson);
		else return getrnk_v(val,ns[rt].lson);
	}
	int getidx_v(ll val,int rt){
		if(!rt)return 0;
		if(ns[rt].val<val)return getidx_v(val,ns[rt].rson);
		int res=getidx_v(val,ns[rt].lson);
		if(!res)res=rt;
		return res;
	}
	void insert(int idx,int &rt){
		int a,b;
		split(rt,getrnk_v(ns[idx].val,rt)-1,a,b);
		merge(a,idx,a),merge(a,b,rt);
	}
	void remove(int idx,int &rt){
		int a,b,c;
		split(rt,getrnk(idx,rt)-1,a,b),split(b,1,b,c);
		merge(a,c,rt);
	}
}fhq;
区间懒标记模板
你只需要按需更改 update 和 pushdown 函数即可。
auto rnd=mt19937_64(time(0));
struct fhqtreap{
    struct node{
        int lson,rson,fa;
        int siz;
        ll pri;
        node(){siz=1,pri=rnd();}
    }ns[N];
    fhqtreap(){ns[0].siz=0;}
    void update(int x){
        ns[x].fa=0;
        ns[x].siz=ns[ns[x].lson].siz+1+ns[ns[x].rson].siz;
        ns[ns[x].lson].fa=ns[ns[x].rson].fa=x;
    }
    void pushdown(int x){
    }
    void split(int x,int k,int &rta,int &rtb){
        if(!x)return rta=rtb=0,void();
        pushdown(x);
        if(k>ns[ns[x].lson].siz)rta=x,split(ns[x].rson,k-ns[ns[x].lson].siz-1,ns[x].rson,rtb);
        else split(ns[x].lson,k,rta,ns[x].lson),rtb=x;
        update(x);
    }
    void merge(int a,int b,int &rt){
        if(!a||!b)return rt=a+b,void();
        if(ns[a].pri>ns[b].pri){
            pushdown(a);
            merge(ns[a].rson,b,ns[a].rson);
            update(a);rt=a;
        }else{
            pushdown(b);
            merge(a,ns[b].lson,ns[b].lson);
            update(b);rt=b;
        }
    }
    int getrnk(int idx,int rt){
        vec<int> v;
        v.pub(idx);
        for(;idx!=rt;idx=ns[idx].fa)v.pub(ns[idx].fa);
        int res=0;
        per(i,v.size()-1,0){
            pushdown(v[i]);
            if(!i||ns[v[i]].rson==v[i-1])res+=ns[ns[v[i]].lson].siz+1;
        }
        return res;
    }
    int getidx(int rnk,int rt){
        pushdown(rt);
        if(ns[ns[rt].lson].siz==rnk-1)return rt;
        if(ns[ns[rt].lson].siz>=rnk)return getidx(rnk,ns[rt].lson);
        else return getidx(rnk-ns[ns[rt].lson].siz-1,ns[rt].rson);
    }
}fhq;
例题
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 i128;
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<int,ll> pil;
typedef pair<ll,int> pli;
template<typename T> using vec=vector<T>;
template<typename T> using grheap=priority_queue<T>;
template<typename T> using lrheap=priority_queue<T,vector<T>,greater<T>>;
#define fir first
#define sec second
#define pub push_back
#define pob pop_back
#define puf push_front
#define pof pop_front
#define chmax(a,b) a=max(a,b)
#define chmin(a,b) a=min(a,b)
#define rep(i,x,y) for(int i=(x);i<=(y);i++)
#define per(i,x,y) for(int i=(x);i>=(y);i--)
#define repl(i,x,y) for(int i=(x);i<(y);i++)
#define file(f) freopen(#f".in","r",stdin);freopen(#f".out","w",stdout);
struct mint {
	int x,mod;
	inline mint(int o=0,int p=998244353) {x=o;mod=p;}
	inline mint & operator=(ll o){return x=o%mod,(x+=mod)>=mod&&(x-=mod),*this;}
	inline mint & operator+=(mint o){return (x+=o.x)>=mod&&(x-=mod),*this;}
	inline mint & operator-=(mint o){return (x-=o.x)<0&&(x+=mod),*this;}
	inline mint & operator*=(mint o){return x=(ll)x*o.x%mod,*this;}
	inline mint & operator^=(ll b){mint res(1);for(;b;b>>=1,*this*=*this)if(b&1)res*=*this;return x=res.x,*this;}
	inline mint & operator^=(mint o){return *this^=o.x;}
	inline mint & operator/=(mint o){return *this*=(o^=(mod-2));}
	inline mint & operator++(){return *this+=1;}
	inline mint & operator--(){return *this-=1;}
	inline mint operator++(int o){mint res=*this;return *this+=1,res;}
	inline mint operator--(int o){mint res=*this;return *this-=1,res;}
	friend inline mint operator+(mint a,mint b){return a+=b;}
	friend inline mint operator-(mint a,mint b){return a-=b;}
	friend inline mint operator*(mint a,mint b){return a*=b;}
	friend inline mint operator/(mint a,mint b){return a/=b;}
	friend inline mint operator^(mint a,ll b){return a^=b;}
	friend inline mint operator^(mint a,mint b){return a^=b;}
	friend inline bool operator<(mint a,mint b){return a.x<b.x;}
	friend inline bool operator>(mint a,mint b){return a.x>b.x;}
	friend inline bool operator<=(mint a,mint b){return a.x<=b.x;}
	friend inline bool operator>=(mint a,mint b){return a.x>=b.x;}
	friend inline bool operator==(mint a,mint b){return a.x==b.x;}
	friend inline istream & operator>>(istream &in,mint &o){ll x;return in>>x,o=x,in;}
	friend inline ostream & operator<<(ostream &ot,mint o){return ot<<o.x,ot;}
};
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int N=1e5+5;
int n,m;
auto rnd=mt19937_64(time(0));
struct fhqtreap{
    struct node{
        int lson,rson,fa;
        int siz;
        ll pri;
        ll val;
        node(){siz=1,pri=rnd(),val=0;}
    }ns[N];
    fhqtreap(){ns[0].siz=0;}
    void update(int x){
        ns[x].fa=0;
        ns[x].siz=ns[ns[x].lson].siz+1+ns[ns[x].rson].siz;
        ns[ns[x].lson].fa=ns[ns[x].rson].fa=x;
    }
    void split(int x,int k,int &rta,int &rtb){
        if(!x)return rta=rtb=0,void();
        if(k>ns[ns[x].lson].siz)rta=x,split(ns[x].rson,k-ns[ns[x].lson].siz-1,ns[x].rson,rtb);
        else split(ns[x].lson,k,rta,ns[x].lson),rtb=x;
        update(x);
    }
    void merge(int a,int b,int &rt){
        if(!a||!b)return rt=a+b,void();
        if(ns[a].pri>ns[b].pri){
            merge(ns[a].rson,b,ns[a].rson);
            update(a);rt=a;
        }else{
            merge(a,ns[b].lson,ns[b].lson);
            update(b);rt=b;
        }
    }
    int getrnk(int idx,int rt){
        int res=ns[ns[idx].lson].siz+1;
        for(;idx!=rt;idx=ns[idx].fa)res+=(idx==ns[ns[idx].fa].rson?ns[ns[ns[idx].fa].lson].siz+1:0);
        return res;
    }
    int getidx(int rnk,int rt){
        if(ns[ns[rt].lson].siz==rnk-1)return rt;
        if(ns[ns[rt].lson].siz>=rnk)return getidx(rnk,ns[rt].lson);
        else return getidx(rnk-ns[ns[rt].lson].siz-1,ns[rt].rson);
    }
    int getrnk_v(ll val,int rt){
        if(!rt)return 1;
        if(ns[rt].val<val)return ns[ns[rt].lson].siz+1+getrnk_v(val,ns[rt].rson);
        else return getrnk_v(val,ns[rt].lson);
    }
    int getidx_v(ll val,int rt){
        if(!rt)return 0;
        if(ns[rt].val<val)return getidx_v(val,ns[rt].rson);
        int res=getidx_v(val,ns[rt].lson);
        if(!res)res=rt;
        return res;
    }
    void insert(int idx,int &rt){
        int a,b;
        split(rt,getrnk_v(ns[idx].val,rt)-1,a,b);
        merge(a,idx,a),merge(a,b,rt);
    }
    void remove(int idx,int &rt){
        int a,b,c;
        split(rt,getrnk(idx,rt)-1,a,b),split(b,1,b,c);
        merge(a,c,rt);
    }
}fhq;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>m;
    int rt=0;
    // rep(i,1,n)cin>>fhq.ns[i].val,insert(i,rt);
    rep(i,1,m){
        int o,x;cin>>o>>x;
        if(o==1)fhq.ns[++n].val=x,fhq.insert(n,rt);
        if(o==2)fhq.remove(fhq.getidx_v(x,rt),rt);
        if(o==3)cout<<fhq.getrnk_v(x,rt)<<"\n";
        if(o==4)cout<<fhq.ns[fhq.getidx(x,rt)].val<<"\n";
        if(o==5)cout<<fhq.ns[fhq.getidx(fhq.getrnk_v(x,rt)-1,rt)].val<<"\n";
        if(o==6)cout<<fhq.ns[fhq.getidx(fhq.getrnk_v(x+1,rt),rt)].val<<"\n";
    }
    return 0;
}
这题其实很适合讲解的。首先,涉及区间操作。其次,左右子树互换,正好演示 getrnk 函数一路 pushdown 下来。而且这就是动态维护区间而非维护可重集的例子。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 i128;
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<int,ll> pil;
typedef pair<ll,int> pli;
template<typename T> using vec=vector<T>;
template<typename T> using grheap=priority_queue<T>;
template<typename T> using lrheap=priority_queue<T,vector<T>,greater<T>>;
#define fir first
#define sec second
#define pub push_back
#define pob pop_back
#define puf push_front
#define pof pop_front
#define chmax(a,b) a=max(a,b)
#define chmin(a,b) a=min(a,b)
#define rep(i,x,y) for(int i=(x);i<=(y);i++)
#define per(i,x,y) for(int i=(x);i>=(y);i--)
#define repl(i,x,y) for(int i=(x);i<(y);i++)
#define file(f) freopen(#f".in","r",stdin);freopen(#f".out","w",stdout);
struct mint {
	int x,mod;
	inline mint(int o=0,int p=998244353) {x=o;mod=p;}
	inline mint & operator=(ll o){return x=o%mod,(x+=mod)>=mod&&(x-=mod),*this;}
	inline mint & operator+=(mint o){return (x+=o.x)>=mod&&(x-=mod),*this;}
	inline mint & operator-=(mint o){return (x-=o.x)<0&&(x+=mod),*this;}
	inline mint & operator*=(mint o){return x=(ll)x*o.x%mod,*this;}
	inline mint & operator^=(ll b){mint res(1);for(;b;b>>=1,*this*=*this)if(b&1)res*=*this;return x=res.x,*this;}
	inline mint & operator^=(mint o){return *this^=o.x;}
	inline mint & operator/=(mint o){return *this*=(o^=(mod-2));}
	inline mint & operator++(){return *this+=1;}
	inline mint & operator--(){return *this-=1;}
	inline mint operator++(int o){mint res=*this;return *this+=1,res;}
	inline mint operator--(int o){mint res=*this;return *this-=1,res;}
	friend inline mint operator+(mint a,mint b){return a+=b;}
	friend inline mint operator-(mint a,mint b){return a-=b;}
	friend inline mint operator*(mint a,mint b){return a*=b;}
	friend inline mint operator/(mint a,mint b){return a/=b;}
	friend inline mint operator^(mint a,ll b){return a^=b;}
	friend inline mint operator^(mint a,mint b){return a^=b;}
	friend inline bool operator<(mint a,mint b){return a.x<b.x;}
	friend inline bool operator>(mint a,mint b){return a.x>b.x;}
	friend inline bool operator<=(mint a,mint b){return a.x<=b.x;}
	friend inline bool operator>=(mint a,mint b){return a.x>=b.x;}
	friend inline bool operator==(mint a,mint b){return a.x==b.x;}
	friend inline istream & operator>>(istream &in,mint &o){ll x;return in>>x,o=x,in;}
	friend inline ostream & operator<<(ostream &ot,mint o){return ot<<o.x,ot;}
};
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int N=1e5+5;
int n,m;
auto rnd=mt19937_64(time(0));
struct fhqtreap{
    struct node{
        int lson,rson,fa;
        int siz;
        ll pri;
        ll val;
        int tag;
        node(){siz=1,pri=rnd(),val=0;}
    }ns[N];
    fhqtreap(){ns[0].siz=0;}
    void update(int x){
        ns[x].fa=0;
        ns[x].siz=ns[ns[x].lson].siz+1+ns[ns[x].rson].siz;
        ns[ns[x].lson].fa=ns[ns[x].rson].fa=x;
    }
    void pushdown(int x){
        if(ns[x].tag){
            swap(ns[ns[x].lson].lson,ns[ns[x].lson].rson);
            swap(ns[ns[x].rson].lson,ns[ns[x].rson].rson);
            ns[ns[x].lson].tag^=1;ns[ns[x].rson].tag^=1;
            ns[x].tag=0;
        }
    }
    void split(int x,int k,int &rta,int &rtb){
        if(!x)return rta=rtb=0,void();
        pushdown(x);
        if(k>ns[ns[x].lson].siz)rta=x,split(ns[x].rson,k-ns[ns[x].lson].siz-1,ns[x].rson,rtb);
        else split(ns[x].lson,k,rta,ns[x].lson),rtb=x;
        update(x);
    }
    void merge(int a,int b,int &rt){
        if(!a||!b)return rt=a+b,void();
        if(ns[a].pri>ns[b].pri){
            pushdown(a);
            merge(ns[a].rson,b,ns[a].rson);
            update(a);rt=a;
        }else{
            pushdown(b);
            merge(a,ns[b].lson,ns[b].lson);
            update(b);rt=b;
        }
    }
    int getrnk(int idx,int rt){
        vec<int> v;
        v.pub(idx);
        for(;idx!=rt;idx=ns[idx].fa)v.pub(ns[idx].fa);
        int res=0;
        per(i,v.size()-1,0){
            pushdown(v[i]);
            if(!i||ns[v[i]].rson==v[i-1])res+=ns[ns[v[i]].lson].siz+1;
        }
        return res;
    }
    int getidx(int rnk,int rt){
        pushdown(rt);
        if(ns[ns[rt].lson].siz==rnk-1)return rt;
        if(ns[ns[rt].lson].siz>=rnk)return getidx(rnk,ns[rt].lson);
        else return getidx(rnk-ns[ns[rt].lson].siz-1,ns[rt].rson);
    }
}fhq;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n>>m;
    int rt=0;
    rep(i,1,n)fhq.ns[i].val=i,fhq.merge(rt,i,rt);
    rep(i,1,m){
        int l,r;cin>>l>>r;
        int a,b,c;
        fhq.split(rt,l-1,a,b);
        fhq.split(b,r-l+1,b,c);
        swap(fhq.ns[b].lson,fhq.ns[b].rson);
        fhq.ns[b].tag^=1;
        fhq.merge(a,b,a);
        fhq.merge(a,c,rt);
    }
    rep(i,1,n)cout<<fhq.ns[fhq.getidx(i,rt)].val<<" ";
    return 0;
}

                
            
        
浙公网安备 33010602011771号