平衡树(splay)模板,包含常规6大操作以及区间翻转

 

P3391 【模板】文艺平衡树

P3369 【模板】普通平衡树

已经测试过上述题目(而且性能较好),可放心食用~~

#include 
using namespace std;
const int N = 100005;
int n;
struct Splay 
{
	int rt, tot, fa[N], ch[N][2], val[N], cnt[N], sz[N];
	//sz[x]:x节点子树的大小;cnt[x]:与x节点权值相同的数的个数;val[x]:x节点的权值 
	int rev[N];//x节点的左右子树是否需要翻转 
	inline void init()
	{
		rt=tot=0;
	}
  	inline void pushup(int x) { sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + cnt[x]; }//更新节点x的sz 
  	inline bool get(int x) { return x == ch[fa[x]][1]; }//x是否是其父亲节点的右儿子 
  	inline void clear(int x) {ch[x][0] = ch[x][1] = fa[x] = val[x] = sz[x] = cnt[x] = 0;}//删除节点x 
  	inline void rotate(int x,int &k)
	{
	    int y=fa[x],z=fa[y],d=ch[y][1]==x;
	    if (y==k)k=x;//k是x的父亲节点 
	    else ch[z][ch[z][1]==y]=x;
	    fa[ch[x][d^1]]=y;fa[y]=x;fa[x]=z;
	    ch[y][d]=ch[x][d^1];ch[x][d^1]=y;
	    pushup(x);pushup(y);
	}
	inline void splay(int x,int &k)//把节点x与节点k互换,随时更新k的值,防止把k旋转到x子树里 
	{
	    while (x!=k)
		{
	        int y=fa[x],z=fa[y];
	        if (y!=k)//双旋操作 
			{
	            if ((ch[z][0]==y)^(ch[y][0]==x))rotate(x,k);
	            else rotate(y,k);
	        }
	        rotate(x,k);
	    }
	}
  	void ins(int x) //插入一个数x 
	{
   		if (!rt) 
		{
	      	val[++tot] = x;cnt[tot]++;
	      	rt = tot;pushup(rt);//新建一个节点(此时树为空) 
	     	return;
    	}
    	int now = rt, f = 0;//now:当前查询到的节点 ,f:cnr的父亲节点 
   		while (1) 
		{
	      	if (val[now] == x) 
			{
		        cnt[now]++;
		        pushup(now);pushup(f);
		        splay(now,rt);
		        break;
      		}
      		f = now;
      		now = ch[now][val[now] < x];//根据BST的性质往下查询 
      		if (!now)//如果平衡树中不存在权值为k的节点,则新建一个节点 
			{
		        val[++tot] = x;cnt[tot]++;
		        fa[tot] = f;
		        ch[f][val[f] < x] = tot;
		        pushup(tot);pushup(f);
		        splay(tot,rt);//每次操作都要splay,保证均摊复杂度能做到logn级别 
		        break;
      		}
    	}
  	}
  	int rk(int x) //查询数x的排名(从小到大) ,只要查到权值等于x的节点,所有小于x的数都在x左子树上。 
	{
	    int res = 0, now = rt;//小于x的数有res个
	    while (1) 
		{
	        if (x < val[now]) now = ch[now][0];//根据BST的性质往下找到x所在的位置 
	        else 
			{
		        res += sz[ch[now][0]];//往右走的时候先累加左子树的数量 
		        if (x == val[now]) 
				{
		          	splay(now,rt);
		          	return res + 1;
		        }
		        res += cnt[now];
		        now = ch[now][1];
	      	}
	    }
    }
  	int kth(int k) //查询第k大的数的权值 
  	{
	    int now = rt;
	    while (1) 
		{
	      	if (ch[now][0] && k <= sz[ch[now][0]]) 
	        	now = ch[now][0];
	        else 
			{
		        k -= cnt[now] + sz[ch[now][0]];
		        if (k <= 0) 
				{
		          splay(now,rt);
		          return val[now];
		        }
		        now = ch[now][1];
	      	}
	    }
  	}
  	//注意!执行前驱后继时要先插入x节点(插入时会自动把他旋转到根节点),然后进行处理,最后再删除x节点。保证每次处理都在根节点进行处理 
	int pre() //查询根节点的前驱,小于根节点权值,且最大的数 
	{
	    int now = ch[rt][0];
	    while (ch[now][1]) now = ch[now][1];
	    splay(now,rt);
	    return now;
	}
	int nxt()//查询根节点的后继,大于根节点权值,且最小的数 
	{
	    int now = ch[rt][1];
	    while (ch[now][0]) now = ch[now][0];
	    splay(now,rt);
	    return now;
	}
  	void del(int k) //删除k节点 
	{
    	rk(k); //这一步的目的是把k旋转到根节点,方便后续处理以及复杂度保证(每一步操作尽量在根节点进行,即用双旋操作缩短深度在处理,防止退化)
    	if (cnt[rt] > 1) 
		{
	      	cnt[rt]--;
	      	pushup(rt);
	      	return;
    	}
	    if (!ch[rt][0] && !ch[rt][1])//树只剩根节点时,树变为空 
		{
		    clear(rt);
		    rt = 0;
		    return;
	    }
	    if (!ch[rt][0]) //如果左子树为空,则以右子树为根 
		{
	      	int now = rt;
	      	rt = ch[rt][1];
	      	fa[rt] = 0;
	     	clear(now);
	     	return;
	    }
    	if (!ch[rt][1])//如果右子树为空,则以左子树为根 
		{
		    int now = rt;
		    rt = ch[rt][0];
		    fa[rt] = 0;
		    clear(now);
		    return;
    	}
		//左右子树均不为空,则以左子树最大权值 的节点做为根节点 (维持BST的性质)
	    int now = rt;
	    int x = pre();
	    splay(x,rt);
	    fa[ch[now][1]] = x;//把x旋转到根节点后,原来根节点的右儿子还未接到x节点 
	    ch[x][1] = ch[now][1];
	    clear(now);//删除原来的根节点 
	    pushup(rt);
  	}
	void pushdown(int x)//下推节点x的标记 
	{
	    if (rev[x])
		{
	        int &lc=ch[x][0],&rc=ch[x][1];
	        swap(lc,rc);
	        rev[lc]^=1;rev[rc]^=1;rev[x]=0;
	    }
	}
	void build(int l,int r,int &rt)//建树,根据初始序列id建 ,默认初始1,2,3,4,5 
	{
	    if (l>r) return;
	    int mid=(l+r)>>1;
	    if (midk)return select(ch[rt][0],k);
	    return select(ch[rt][1],k-sum);
	}
	void rever(int L,int R)//翻转区间L,R 
	{
	    splay(L,rt);splay(R,ch[L][1]);
	    rev[ch[R][0]]^=1;
	}
	void print(int rt)//符合BST性质的中序遍历 
	{
	    pushdown(rt);//下推懒标记 
	    if (ch[rt][0])print(ch[rt][0]);
	    if (2<=rt && rt<=n+1)printf("%d ",rt-1);
	    if (ch[rt][1])print(ch[rt][1]);
	}
}Tree;
 
int main(){
    int m;
    scanf("%d%d",&n,&m);
    Tree.rt=(3+n)>>1;Tree.build(1,n+2,Tree.rt);//将询问区间[l,r]转换成[l+1,r+1],那么我们翻转l-1,r+1时就不需要特判了。
    for (int i=1;i<=m;i++)
	{
        int L,R;
        scanf("%d%d",&L,&R);
        L=Tree.select(Tree.rt,L);R=Tree.select(Tree.rt,R+2);
        Tree.rever(L,R);
    }
    Tree.print(Tree.rt);//打印最终序列的id 
    return 0;
}
/*
int main() {
	int opt, x;
	scanf("%d",&n);
	while(n--)
	{
	    scanf("%d%d", &opt, &x);
	    if (opt == 1)Tree.ins(x);
	    else if (opt == 2)Tree.del(x);
	    else if (opt == 3)printf("%d\n", Tree.rk(x));
	    else if (opt == 4)printf("%d\n", Tree.kth(x));
	    else if (opt == 5)Tree.ins(x), printf("%d\n", Tree.val[Tree.pre()]), Tree.del(x);
	    else Tree.ins(x), printf("%d\n", Tree.val[Tree.nxt()]), Tree.del(x);
	}
  return 0;
}
*/

 

你可能感兴趣的:(数据结构————平衡树)