Treap树    =     tree+heap

数和堆的集合,每个节点有值val,也有优先级key,那么这棵树的形态就被确定了,和插入顺序无关了(有赖于优先级

避免退化成链:再生成节点时,随机生成优先级,然后插入时动态调整

 有旋treap:依靠旋转来维持heap的平衡

1、FHQ treap又称无旋treap,没有旋转操作,使用分裂和合并两个操作维护树的平衡

struct node{
	int l,r;
	int val;
	int key;
	int size;
}tr[N];
int root,idx;
int newnode(int v){
}
void pushup(){  //向上更新 
}
void split(int p,int v,int &x,int &y){ //分裂操作 注意在递归的过程中,连接了分裂后的新边 
} 
int merg(int x,int y){  //合并,根据key的大小,注意在递归的过程中,连接了分裂后的新边 
} 
void inser(int v){ //插入节点,先分裂,再合并,连续两次合并 
} 
void del(int v){ //删除操作,先分裂、再合并 
}
int get_k(int p,int k){ //返回第k个节点 
} 
void get_pre(int v){ //找前驱 
} 
void get_suc(int v){ //找后继 
}
void get_rank(int v){//排名 
} 
void get_val(int k){//数值 
}

  

P3369 【模板】普通平衡树

 

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1e5+10; 
struct node{
	int l,r;
	int val;   //树的权值 
	int key;   //堆的随机值
	int size;
}tr[maxn];
int root,idx;
int newnode(int &x,int v){
	x=++idx;
	tr[x].val=v;
	tr[x].key=rand();
	tr[x].size=1;
}
void pushup(int p){  //向上更新 
	tr[p].size=tr[tr[p].l].size+tr[tr[p].r].size+1; 
}
void split(int p,int v,int &x,int &y){ //分裂操作 注意在递归的过程中,连接了分裂后的新边 
	if(!p) {x=y=0;return;
	}
	if(tr[p].val<=v){
		x=p;
		split(tr[x].r,v,tr[x].r,y);
		pushup(x);
	}
	else{
		y=p;
		split(tr[y].l,v,x,tr[y].l);
		pushup(y);;
	}
} 
int merg(int x,int y){  //合并,根据key的大小,注意在递归的过程中,连接了分裂后的新边 
	if(!x||!y) return x+y;
	if(tr[x].key<tr[y].key){
		tr[x].r=merg(tr[x].r,y);
		pushup(x);
		return x;
	}
	else{
		tr[y].l=merg(x,tr[y].l);
		pushup(y);
		return y;
	}
} 
void inser(int v){ //插入节点,先分裂,再合并,连续两次合并 
	int x,y,z;
	split(root,v,x,y);
	newnode(z,v);
	root=merg(merg(x,z),y);
} 
void del(int v){ //删除操作,先分裂、再合并 
	int x,y,z;
	split(root,v,x,z);
	split(x,v-1,x,y);
	y=merg(tr[y].l,tr[y].r);
	root=merg(merg(x,y),z);
}
//int get_k(int p,int k){ //返回第k个节点
//	
//} 

int getrank(int v){//排名 
	int x,y;
	split(root,v-1,x,y);
	int ans=tr[x].size+1;
	root=merg(x,y);
	return ans; 
} 
int getval(int root,int v){//数值 
	if(v==tr[tr[root].l].size+1) return tr[root].val;
	else if(v<=tr[tr[root].l].size) return getval(tr[root].l,v);
	else return getval(tr[root].r,v-tr[tr[root].l].size-1);
}
int getpre(int v){ //找前驱 
	int x,y,s,ans;
	split(root,v-1,x,y);
	s=tr[x].size;
	ans=getval(x,s);
	root=merg(x,y);
	return ans;
} 
int getnex(int v){ //找后继 
	int x,y,ans;
	split(root,v,x,y);
	ans=getval(y,1);
	root=merg(x,y);
	return ans;
}
int main(){
	int n,op,v;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d %d",&op,&v);
		if(op==1) inser(v);
		else if(op==2) del(v);
		else if(op==3) printf("%d\n",getrank(v));
		else if(op==4) printf("%d\n",getval(root,v));
		else if(op==5) printf("%d\n",getpre(v));
		else printf("%d\n",getnex(v));
	}
	
	return 0;
}

  

文艺平衡树

还是有旋转操作的

// Luogu P3391 【模板】文艺平衡树
#include <iostream>
using namespace std;

const int N=100010;
struct node{
  int l,r; //左右儿子
  int val; //树的权值 
  int key; //堆的随机值
  int size; //子树大小
  int tag; //懒标记
}tr[N];
int n,m,root,idx;

int newnode(int v){
  tr[++idx].val=v;
  tr[idx].key=rand();
  tr[idx].size=1;
  return idx;
}
void pushup(int p){
  tr[p].size=tr[tr[p].l].size
        +tr[tr[p].r].size+1;
}
void pushdown(int p){
  if(!tr[p].tag||!p) return;
  swap(tr[p].l, tr[p].r);
  tr[tr[p].l].tag ^= 1;
  tr[tr[p].r].tag ^= 1;
  tr[p].tag = 0;
}
void split(int p,int k,int &x,int &y){
  if(!p) {x=y=0; return;}
  pushdown(p);
  if(k>tr[tr[p].l].size){
    k-=tr[tr[p].l].size+1;
    x=p;
    split(tr[p].r,k,tr[p].r,y);
  } 
  else{
    y=p;
    split(tr[p].l,k,x,tr[p].l);
  }
  pushup(p);
}
int merge(int x,int y){
  if(!x||!y) return x+y;
  if(tr[x].key<tr[y].key){
    pushdown(x);
    tr[x].r=merge(tr[x].r,y);
    pushup(x); return x;
  }
  else{
    pushdown(y);
    tr[y].l=merge(x,tr[y].l);
    pushup(y); return y;
  }
}
void reverse(int l,int r){
  int x,y,z;
  split(root,r,x,z);
  split(x,l-1,x,y);
  tr[y].tag ^= 1; //标记
  root=merge(merge(x,y),z);
}
void output(int p){
  if(!p) return;
  pushdown(p);
  output(tr[p].l);
  printf("%d ",tr[p].val);
  output(tr[p].r);
}
int main(){
  srand(time(0));
  scanf("%d%d",&n,&m);
  for(int i=1;i<=n;i++)
    root=merge(root,newnode(i));
  for(int x,y,i=1;i<=m;i++){
    scanf("%d%d",&x,&y);
    reverse(x, y);
  }
  output(root);
  return 0;
}

  

删除:如果有两个子节点,找到优先级大的,把x向反方向旋转,也就是把x向树的下层调整,直到旋转到叶子节点

!很多题目涉及名次树

常用操作:

struct node,旋转rotate,插入insert(),查找第k大的数kth(),查询某个数find()【名次树】

少林 hdu 4585

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll inf = 4e18+10;
const int mod = 1000000007;
const int mx = 5e6+5; //check the limits, dummy
typedef pair<int, int> pa;
const double PI = acos(-1);
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
#define swa(a,b) a^=b^=a^=b
#define re(i,a,b) for(int i=(a),_=(b);i<_;i++)
#define rb(i,a,b) for(int i=(b),_=(a);i>=_;i--)
#define clr(a) memset(a, 0, sizeof(a))
#define lowbit(x) ((x)&(x-1))
#define mkp make_pair
void sc(int& x) { scanf("%d", &x); }void sc(int64_t& x) { scanf("%lld", &x); }void sc(double& x) { scanf("%lf", &x); }void sc(char& x) { scanf(" %c", &x); }void sc(char* x) { scanf("%s", x); }
int  m, n,k,sum=0,ans=0,t;
int id[mx];
struct node
{
    int size;//以这个节点为根的子树的节点总数,用于名次树
    int rank;//优先级
    int key;//键值
    node *son[2];//son[0]左儿子,son[1]右儿子
    bool operator<(const node& a)const { return rank < a.rank;}
    int cmp(int x)const {
        if (x == key)return -1;
        return x < key ? 0 : 1;
    }
    void update() {
        size = 1;
        if (son[0] != NULL)size += son[0]->size;
        if (son[1] != NULL)size += son[1]->size;
    }
};
void rotate(node* &o, int d) {//d=0,左旋,d=1,右旋
    node *k = o->son[d ^ 1];//d^1等价于1-d,但是更快
    o->son[d ^ 1] = k->son[d];
    k->son[d] = o;
    o->update();
    k->update();
    o = k;
}
void insert(node* &o, int x) {//把x插入树中
    if (o == NULL) {
        o = new node();
        o->son[0] = o->son[1] = NULL;
        o->rank = rand();
        o->key = x;
        o->size = 1;
    }
    else {
        int d = o->cmp(x);
        insert(o->son[d],x);
        o->update();
        if (o < o->son[d]);
        rotate(o, d ^ 1);
    }
}
int kth(node* o, int k) {//返回第k大的数
    if (o == NULL || k <= 0 || k > o->size)
        return -1;
    int s = o->son[1] == NULL ? 0 : o->son[1]->size;
    if (k == s + 1)return o->key;
    else if (k <= s)return kth(o->son[1], k);
    else return kth(o->son[0], k - s - 1);
}
int find(node* o, int k) {//返回元素k的名次
    if (o == NULL)
        return -1;
    int d = o->cmp(k);
    if (d == -1)
        return o->son[1] == NULL ? 1 : o->son[1]->size + 1;
    else if (d == 1)return find(o->son[d],k);
    else {
        int tmp = find(o->son[d], k);
        if (tmp == -1)
            return -1;
        else {
            return o->son[1] == NULL ? tmp + 1 : tmp + 1 + o ->son[1]->size;
        }
    }
}
int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    while (cin>>n&&n)
    {
        srand(time(NULL));
        int k, g;
        cin >> k >> g;
        node* root = new node();
        root->son[0] = root-> son[1] = NULL;
        root->rank = rand();
        root->key = g;
        root->size = 1;
        id[g] = k;
        cout << k << ' ' << 1<<endl;
        re(i, 2, n + 1) {
            cin >> k >> g;
            id[g] = k;
            insert(root, g);
            t = find(root, g);//返回新和尚的名次
            int ans1, ans2;
            ans1 = kth(root, t - 1);//前一名的老和尚
            ans2 = kth(root, t + 1);//后一名的老和尚
            if (ans1 != -1 && ans2 != -1)
            {
                ans = ans1 - g >= g - ans2 ? ans2 : ans1;
            }
            else if (ans1 == -1)
                ans = ans2;
            else ans = ans1;
            cout << k << ' ' << id[ans] << endl;
        }
    }
    return 0;
}

  

 posted on 2023-06-03 13:26  shirlybabyyy  阅读(36)  评论(0)    收藏  举报