BZOJ 3224 Tyvj 1728 普通平衡树模板

题目链接:

https://www.lydsy.com/JudgeOnline/problem.php?id=3224

题目大意:

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)

思路:

splay:http://www.cnblogs.com/BCOI/p/9010229.html

treap:https://www.cnblogs.com/BCOI/p/9072444.html

  1 #include<bits/stdc++.h>
  2 #define IOS ios::sync_with_stdio(false);//不可再使用scanf printf
  3 #define Max(a, b) ((a) > (b) ? (a) : (b))//禁用于函数,会超时
  4 #define Min(a, b) ((a) < (b) ? (a) : (b))
  5 #define Mem(a) memset(a, 0, sizeof(a))
  6 #define Dis(x, y, x1, y1) ((x - x1) * (x - x1) + (y - y1) * (y - y1))
  7 #define MID(l, r) ((l) + ((r) - (l)) / 2)
  8 #define lson ((o)<<1)
  9 #define rson ((o)<<1|1)
 10 #pragma comment(linker, "/STACK:102400000,102400000")//栈外挂
 11 using namespace std;
 12 inline int read()
 13 {
 14     int x=0,f=1;char ch=getchar();
 15     while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
 16     while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
 17     return x*f;
 18 }
 19 
 20 typedef long long ll;
 21 const int maxn = 1000000 + 10;
 22 const int MOD = 1000000007;//const引用更快,宏定义也更快
 23 const int INF = 1e9 + 7;
 24 const double eps = 1e-6;
 25 
 26 struct node
 27 {
 28     int fa;//记录父节点
 29     int ch[2];//ch[0]表示左儿子 ch[1]表示右儿子
 30     int val;//节点权值
 31     int Size;//记录节点子树大小(包括该节点)
 32     int cnt;//记录同样权值的元素个数
 33     int mark;//记录反转区间标记(普通平衡树不用)
 34 }t[maxn];
 35 int root = 0;//根节点
 36 int cnt = 0;//当前节点数目
 37 bool get(int x)//判断一个节点是左节点还是右节点
 38 {
 39     return t[t[x].fa].ch[1] == x;//右节点返回1 左节点返回0
 40 }
 41 void up(int x)//重新统计节点x的size
 42 {
 43     t[x].Size = t[t[x].ch[0]].Size + t[t[x].ch[1]].Size + t[x].cnt;
 44 }
 45 void Rotate(int x)//节点x与父节点旋转
 46 {
 47     int fa = t[x].fa, gfa = t[fa].fa;
 48     int d1 = get(x), d2 = get(fa);//加上d1 d2均表示左儿子
 49     t[fa].ch[d1] = t[x].ch[d1 ^ 1]; t[t[x].ch[d1 ^ 1]].fa = fa;//父节点的左儿子设置成左儿子的右儿子(双向设置)
 50     t[gfa].ch[d2] = x; t[x].fa = gfa;//祖父节点的左儿子设置成父节点的左儿子(双向)
 51     t[fa].fa = x; t[x].ch[d1^1] = fa;//左儿子的右儿子设置成父节点(双向)
 52     up(fa); up(x);
 53 }
 54 void splay(int x, int goal)//x旋转到goal下面
 55 {
 56     while(t[x].fa != goal)
 57     {
 58         int fa = t[x].fa, gfa = t[fa].fa;
 59         int d1 = get(x), d2 = get(fa);
 60         if(gfa != goal)
 61         {
 62             if(d1 == d2)Rotate(fa);//同侧,先旋父节点(双旋)
 63             else Rotate(x);//异侧 直接选
 64         }
 65         Rotate(x);//再向上旋一次
 66     }
 67     if(goal == 0)root = x;
 68 }
 69 void Insert(int val)
 70 {
 71     int node = root, fa = 0;
 72     while(node && t[node].val != val)
 73         fa = node, node = t[node].ch[t[node].val < val];
 74     if(node)t[node].cnt++;//节点存在
 75     else
 76     {
 77         node = ++cnt;
 78         if(fa)t[fa].ch[t[fa].val < val] = node;
 79         t[node].fa = fa;
 80         t[node].Size = t[node].cnt = 1;
 81         t[node].val = val;
 82     }
 83     splay(node, 0);//将新节点旋到根来维护splay的子树
 84 }
 85 int kth(int k)//查询第k大的数
 86 {
 87     int node = root;
 88     while(1)
 89     {
 90         int son = t[node].ch[0];
 91         if(k <= t[son].Size)node = son;
 92         else if(k > t[son].Size + t[node].cnt)
 93         {
 94             k -= t[son].Size + t[node].cnt;
 95             node = t[node].ch[1];
 96         }
 97         else return t[node].val;
 98     }
 99 }
100 int Find(int val)//直接调用Find(INF)或者调用Find(-INF)可以得到最大值和最小值编号
101 {
102     int node = root;
103     while(t[node].val != val && t[node].ch[t[node].val < val])
104         node = t[node].ch[t[node].val < val];
105     return node;
106 }
107 int get_rank(int val)//查询一个数的排名
108 {
109     splay(Find(val), 0);
110     return t[t[root].ch[0]].Size + 1;
111 }
112 int get_pre(int val, int kind)//kind = 0 查询前驱 kind = 1查询后继
113 {
114     splay(Find(val), 0);
115     int node = root;
116     if(t[node].val < val && kind == 0)return node;
117     if(t[node].val > val && kind == 1)return node;//根节点就是前驱/后继的情况
118     node = t[node].ch[kind];
119     while(t[node].ch[kind ^ 1])node = t[node].ch[kind ^ 1];//否则在子树中寻找最值
120     return node;//返回的是编号 具体的值调用需要用t[node].val
121 }
122 void delet(int val)
123 {
124     int last = get_pre(val, 0);//由于此处查询了前驱和后缀 可能会查询到最小值的前驱或者最大值的后缀
125     int next = get_pre(val, 1);//必须事先加入INF 和-INF 不然会出错
126     splay(last, 0);
127     splay(next, last);
128     if(t[t[next].ch[0]].cnt > 1)
129     {
130         t[t[next].ch[0]].cnt--;
131         splay(t[next].ch[0], 0);
132     }
133     else t[next].ch[0] = 0;
134 }
135 int main()
136 {
137     int n, x, y;
138     Insert(-INF);
139     Insert(INF);
140     scanf("%d", &n);
141     while(n--)
142     {
143         scanf("%d%d", &x, &y);
144         if(x == 1)Insert(y);
145         if(x == 2)delet(y);
146         if(x == 3)printf("%d\n", get_rank(y)- 1);//排名减一是因为要减去负无穷这个数
147         if(x == 4)printf("%d\n", kth(y + 1));//排名是y+1 因为加上负无穷这个数
148         if(x == 5)printf("%d\n", t[get_pre(y, 0)].val);
149         if(x == 6)printf("%d\n", t[get_pre(y, 1)].val);
150     }
151     return 0;
152 }

 

posted @ 2018-09-19 01:30  _努力努力再努力x  阅读(118)  评论(0编辑  收藏  举报