[模板]洛谷T3369 普通平衡树 链表&普通Treap

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<cstdlib>
  5 #include<ctime>
  6 #include<cmath>
  7 #include<string>
  8 #include<stack>
  9 #include<queue>
 10 #include<vector>
 11 #include<map>
 12 #include<algorithm>
 13 using namespace std;
 14 struct node{
 15     int key,yx;  //键值,优先级(小根堆)
 16     int size,num;  //子树元素总数,当前节点元素总数
 17     node *ch[2];  //左右子树指针
 18 };
 19 void rotate(node* &,bool);  //旋转
 20 void insert(node* &,int);  //插入
 21 void del(node* &,int);  //删除
 22 int kth(node *,int);  //求第k大元素
 23 int rank(node *,int);  //求元素排名
 24 void pre(node *,int);  //求前驱
 25 void succ(node *,int);  //求后继
 26 node *root=NULL;
 27 int ans;
 28 int n;
 29 int f,x;
 30 int main(){
 31     srand(time(0));
 32     scanf("%d",&n);
 33     while(n--){
 34         scanf("%d%d",&f,&x);
 35         switch(f){
 36             case 1:insert(root,x);break;
 37             case 2:del(root,x);break;
 38             case 3:printf("%d\n",rank(root,x));break;
 39             case 4:printf("%d\n",kth(root,x));break;
 40             case 5:{
 41                 pre(root,x);
 42                 printf("%d\n",ans);
 43                 break;
 44             }
 45             case 6:{
 46                 succ(root,x);
 47                 printf("%d\n",ans);
 48                 break;
 49             }
 50         }
 51     }
 52     return 0;
 53 }
 54 void rotate(node* &p,bool f){
 55     node *t=p->ch[f^1];
 56     p->ch[f^1]=t->ch[f];  //改变t的子树的位置
 57     t->ch[f]=p;  //将t旋转至p上方
 58     p->size=p->num;
 59     if(p->ch[0]!=NULL)p->size+=p->ch[0]->size;
 60     if(p->ch[1]!=NULL)p->size+=p->ch[1]->size;
 61     t->size=t->num;
 62     if(t->ch[0]!=NULL)t->size+=t->ch[0]->size;
 63     if(t->ch[1]!=NULL)t->size+=t->ch[1]->size;  //维护节点信息,自底向上先算p再算t,Very important~
 64     p=t;  //将旋转上去的t节点作为当前子树新的根节点,使用引用方式传递
 65 }
 66 void insert(node* &p,int x){
 67     if(p==NULL){
 68         p=(node *)malloc(sizeof(node));
 69         p->key=x;
 70         p->yx=rand();
 71         p->size=p->num=1;
 72         p->ch[0]=p->ch[1]=NULL;
 73         return;
 74     }  //新建节点,在下不知构造函数为何物,只好酱紫了。。。
 75     if(p->key==x){
 76         p->size++;
 77         p->num++;
 78         return;
 79     }  //重复元素直接在相应节点累加元素个数
 80     if(x<p->key){
 81         insert(p->ch[0],x);  //递归插入左子树
 82         if(p->ch[0]->yx<p->yx)rotate(p,1);  //若插入的节点优先级小于当前子树的根节点则将其旋转至根节点上方
 83         else p->size++;  //这样写的正确性证明:1.如果需要旋转,则当前子树的所有节点的信息都已维护完成;2.如果不需旋转,那么在将元素递归插入子树后,只有当前子树的根节点的信息还未更新,那么就将其更新即可
 84     }
 85     else{
 86         insert(p->ch[1],x);
 87         if(p->ch[1]->yx<p->yx)rotate(p,0);
 88         else p->size++;
 89     }  //同理
 90 }
 91 void del(node* &p,int x){
 92     if(p==NULL)return;  //本题的数据保证不会删到空节点,不过写了也无所谓
 93     if(x==p->key){  //当前节点是要删除的元素
 94         if(p->num>1){
 95             p->size--;
 96             p->num--;
 97             return;
 98         }  //若元素重复次数大于1,则减少元素个数即可
 99         else{  //需要删除节点
100             if(p->ch[0]==NULL){
101                 node *t=p;
102                 p=p->ch[1];
103                 free(t);
104                 return;
105             }  //若左子树为空则直接用右子树替代当前节点
106             else if(p->ch[1]==NULL){
107                 node *t=p;
108                 p=p->ch[0];
109                 free(t);
110                 return;
111             }  //若右子树为空则直接用左子树替代当前节点
112             else{  //左右子树均非空,则将当前节点向下旋转,并递归在子树中删除(同时维护小根堆的性质)
113                 if(p->ch[0]->yx<p->ch[1]->yx){  //左子树优先级小于右子树优先级
114                     rotate(p,1);  //将左子树向上旋转,作为当前树新的根节点
115                     del(p->ch[1],x);  //递归在右子树中删除
116                 }
117                 else{
118                     rotate(p,0);
119                     del(p->ch[0],x);
120                 }  //同理
121                 p->size--;  //递归删除完成后,还剩当前根节点的信息未更新,则将其更新即可
122             }
123         }
124     }
125     else{  //当前节点不是要删除的元素
126         if(x<p->key)del(p->ch[0],x);  //递归在左子树中删除
127         else del(p->ch[1],x);  //递归在右子树中删除
128         p->size--;  //递归删除完成后,还剩当前根节点的信息未更新,则将其更新即可
129     }
130 }
131 int kth(node *p,int x){
132     int s=0;  //记录左子树节点数量
133     if(p->ch[0]!=NULL)s=p->ch[0]->size;  //这样写以防RE
134     if(x<=s)return kth(p->ch[0],x);  //查询节点位于左子树内,则其在左子树的排名即是在当前树的排名
135     else if(x<=s+p->num)return p->key;  //当前节点即为所求
136     else return kth(p->ch[1],x-s-p->num);  //查询节点位于右子树内,则其在右子树的排名:当前树排名-左子树元素数-当前树根节点元素数
137 }
138 int rank(node *p,int x){
139     int s=0;
140     if(p->ch[0]!=NULL)s=p->ch[0]->size;  //同理
141     if(x<p->key)return rank(p->ch[0],x);  //查询节点位于左子树内,则其在当前树的排名即是在左子树的排名
142     else if(x==p->key)return s+1;  //找到待查询节点,则其在当前树的排名为:左子树元素数+1
143     else return s+p->num+rank(p->ch[1],x);  //查询节点位于右子树内,则其在当前树的排名为:左子树元素数+当前树根节点元素数+其在右子树的排名
144 }
145 void pre(node *p,int x){
146     if(p==NULL)return;  //递归边界防止RE
147     if(p->key<x){  //当前节点键值为可行解
148         ans=p->key;  //保存可行解
149         pre(p->ch[1],x);  //尝试寻找更优解
150     }
151     else pre(p->ch[0],x);  //当前节点键值不是可行解则回退
152 }
153 void succ(node *p,int x){
154     if(p==NULL)return;
155     if(p->key>x){
156         ans=p->key;
157         succ(p->ch[0],x);
158     }
159     else succ(p->ch[1],x);
160 }  //与pre同理
时间: 2025-01-04 07:38:53

[模板]洛谷T3369 普通平衡树 链表&普通Treap的相关文章

[模板]洛谷T3369 普通平衡树 链表&amp;递归版、无父指针版Splay

结构体node定义:呐...因为不是Treap,所以就不必定义优先级yx了: 这次为了代码简短,总算是把判断子树方向函数(cmp)和节点信息维护函数(maintain)封在了结构体里. 旋转函数rotate:与Treap相比,没有任何变化,就是写得简短了一些. 插入函数insert:Treap时需要对违反堆性质的节点进行上浮的操作现在不需要了,只需同普通BST一样直接插入即可. 插完后,对刚插入的节点执行Splay操作. 删除函数del:针对待删除节点左右子树均非空的情况,Treap做法是将左右

[模板]洛谷T3391 文艺平衡树 链表&amp;递归版、无父指针版Splay

指针大法好 无父指针Splay大法好 大佬们的"改变旋转方向"萌新表示不懂,于是就自己乱搞出了下面的搞法... 代码如下,萌新写的丑,诸位大佬见谅QwQ~ 1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<ctime> 5 #include<cstdlib> 6 #include<ctime> 7 8 #include<

洛谷P1160 队列安排 链表

洛谷P1160 队列安排   链表 1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <cstdlib> 5 #include <string> 6 #include <algorithm> 7 #include <iomanip> 8 #include <iostream> 9 using namespace std

【模板】LIS模板 洛谷P1091 [NOIP2004提高组]合唱队形 [2017年4月计划 动态规划11]

以题写模板. 写了两个:n^2版本与nlogn版本 P1091 合唱队形 题目描述 N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形. 合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足T1<...<Ti>Ti+1>…>TK(1<=i<=K). 你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形. 输入输出格

洛谷3391 文艺平衡树 平衡树模板95

上次被火星人prefix吊打,突然发现已经不会写splay了 于是来道模板题 区间反转? 打一些lazy标记,感觉和线段树没有差太多,而且交换左右儿子这操作真是妙 lazy标记 下传的时间要注意有些东西会变 1 #include <bits/stdc++.h> 2 #define N 500000 3 #define mid (l+r>>1) 4 using namespace std; 5 int n,m,x,y; 6 struct spla 7 { 8 int fa[N],c[

最小费用最大流基础模板(洛谷3381)

如题,给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用. 输入格式: 第一行包含四个正整数N.M.S.T,分别表示点的个数.有向P3381 [模板]最小费用最大流边的个数.源点序号.汇点序号. 接下来M行每行包含四个正整数ui.vi.wi.fi,表示第i条有向边从ui出发,到达vi,边权为wi(即该边最大流量为wi),单位流量的费用为fi. 输出格式: 一行,包含两个整数,依次为最大流量和在最大流量情况下的最小费用. 输入样例#1:

[模板]洛谷T3373 线段树 模板2

此题相对于模板一,加了个区间乘,于是在模板一的基础上需要多开个数组(记录乘法懒标记).多写个函数(区间乘),还有要把懒标记下放函数做些修改. 变量定义: sum[]:线段树节点对应区间的元素总和: addv[]:线段树节点对应区间的所有元素待加的值(懒标记),初值全部设为0: mulv[]:线段树节点对应区间的所有元素待乘的值(懒标记),初值全部设为1. 过程说明: 建树(Build): 同模板一... 懒标记下放(Push_down): 原理解释: 1.当对某区间执行加法操作时,由于加法优先级

[模板]洛谷T2042 NOI2005 维护数列 Splay

PS:某大佬讲,当心情特别好or特别不好的时候,可以来攻略这个题...果然萌新足足被这题恶心了半个月... 进入正题: 这道题题意如此之裸-Splayの究极进化,那么就没有什么好讲的,直接说搞法好了... 为了代码写起来方便,萌新封装得可能有些过,若不合诸位大佬的口味还请见谅~ 节点node结构体定义: key:节点原值:size:子树大小:ch[2]:子树指针: set_pd:记录是否打了MAKE-SAME的懒标记:setv:MAKE-SAME的修改值:turn:记录是否旋转: sum:子树元

[模板]洛谷T3374 树状数组 模板1

1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cmath> 5 #include<ctime> 6 #include<cstdlib> 7 8 #include<algorithm> 9 #include<string> 10 #include<stack> 11 #include<queue>