zoukankan      html  css  js  c++  java
  • 平衡树Splay

    LuoguP3369

    这道题啊。。。

    一开始学习的时候遇到了一篇讲的超级好的博客:rentenglong 的博客

    这篇大家一定要去瞅瞅,讲的炒鸡详细(就是代码有点锅。。。)

    后来找到一份和TA码风差不多的,才完善了一哈qwq

    不过最终还是过了

    Code:

      1 #include <bits/stdc++.h>
      2 #define root e[0].son[1]
      3 using namespace std;
      4 int read() {
      5     int re = 0, f = 1;
      6     char ch = getchar();
      7     while (ch < '0' || ch > '9') {if (ch == '-') f = -f; ch = getchar();}
      8     while ('0' <= ch && ch <= '9') {re = re * 10  + ch - '0'; ch = getchar();}
      9     return re * f;
     10 }
     11 const int N = 1e5 + 3;
     12 const int INF = 1e7 + 3;
     13 int n, cnt;
     14 struct node{
     15     int v, father;//储存键值,父亲节点 
     16     int son[2];//存储左右孩子,son[0]为左,son[1]为右 
     17     int sum;//存储这个节点子树共有多少 元素 (元素包括sum + recy) 
     18     int recy;//存储相同键值重复多少次 
     19 }e[N];
     20 void update(int x) {//合并 
     21     e[x].sum = e[e[x].son[0]].sum + e[e[x].son[1]].sum + e[x].recy;
     22 }
     23 int identify(int x) {//确定当前节点与父亲节点的关系 
     24     return e[e[x].father].son[0] == x ? 0 : 1;
     25 }
     26 void connect(int x, int fa, int son) {//连接两个节点 
     27     e[x].father = fa;
     28     e[fa].son[son] = x;
     29 }
     30 void rotate(int x) {//核心函数之一,旋转 
     31     int y = e[x].father;
     32     int mroot = e[y].father;
     33     int mrootson = identify(y);
     34     int yson = identify(x);
     35     int B = e[x].son[yson ^ 1];
     36     connect(B, y, yson); 
     37     connect(y, x, (yson ^ 1));
     38     connect(x, mroot, mrootson);
     39     update(y);//y在上,先更新y 
     40     update(x);
     41 }
     42 void splay(int at, int to) {//核心函数 
     43     int tofa = e[to].father;
     44     while (e[at].father != tofa) {
     45         int fa = e[at].father;
     46         int gfa = e[fa].father;
     47         if (gfa != tofa) {
     48             if (identify(at) != identify(fa)) {
     49                 rotate(at);//当自己的父亲与爷爷不在一条直线上时,先旋转自己 
     50             } else {
     51                 rotate(fa);//当自己的父亲与爷爷在一条直线上时,先旋转父亲 
     52             }
     53         }
     54         rotate(at);
     55     }
     56     if (to == root) root = at;
     57 }
     58 int newpoint(int v, int fa) {//添加新节点 
     59     e[++cnt].father = fa;
     60     e[cnt].v = v;
     61     e[cnt].sum = e[cnt].recy = 1;
     62     return cnt;
     63 }
     64 void Insert(int x) {
     65     int now = root;
     66     if(!root){//特判没根的情况 
     67         newpoint(x, 0);
     68         root = cnt;
     69     } else {
     70         while (true) {
     71             e[now].sum++;//经过的点子树中元素都要++ 
     72             if (e[now].v == x) {
     73                 e[now].recy++;//重复 
     74                 splay(now, root);
     75                 return;
     76             }
     77             int next = x < e[now].v ? 0 : 1;//判断该走左孩子还是右孩子 
     78             if (!e[now].son[next]) {
     79                 int add = newpoint(x, now);
     80                 e[now].son[next] = add;
     81                 splay(add, root);
     82                 return;
     83             }
     84             now = e[now].son[next];
     85         }
     86     }
     87 }
     88 int find(int v) {//查询键值为v的点的位置 
     89     int now = root;
     90     while (true) {
     91         if (e[now].v == v) {
     92             splay(now, root);//划重点,下面有用 
     93             return now;
     94         }
     95         int next = v < e[now].v ? 0 : 1;
     96         if (!e[now].son[next]) {
     97             return 0;
     98         }
     99         now = e[now].son[next];
    100     }
    101 }
    102 void delet(int x) {//删除节点 
    103     int now = find(x);//注意find()函数中已经将x转为根节点 
    104     if (!now) {
    105         return;
    106     }
    107     if (e[now].recy > 1) {//如果有重复,直接减去即可 
    108         e[now].recy--;
    109         e[now].sum--;
    110     } else {
    111         if (!e[now].son[0] && !e[now].son[1]) {//如果没有左右孩子,说明就一个点 
    112             root = 0;
    113         } else if (!e[now].son[0]) {//如果没有左孩子,直接将右孩子提为根节点 
    114             root = e[now].son[1];
    115             e[root].father = 0;
    116         } else {//左右孩子都有,先将左子树中最大的节点转到左孩子的位置,然后提为根节点,把右孩子连到该节点之下 
    117             int lef = e[now].son[0];
    118             while (e[lef].son[1]) {//找左子树中最大的节点 
    119                 lef = e[lef].son[1];
    120             }
    121             splay(lef, e[now].son[0]);
    122             connect(e[now].son[1], lef, 1);
    123             connect(lef, 0, 1);
    124             update(lef);
    125         }
    126     }
    127 }
    128 int _rank(int v) {
    129     int now = find(v);//因为find()已经将v转为根节点,所以直接求即可 
    130     return e[e[now].son[0]].sum + 1;
    131 }
    132 int arank(int x) {
    133     int now = root;
    134     while (true) {
    135         int used = e[now].sum - e[e[now].son[1]].sum;
    136         //如果x大于左子树元素,并且小于等于当前点的左子树的sum加上本节点的recy的值,那么当前的点就是要找的点 
    137         //因为此时说明该x是当前节点重复值中的一个 
    138         if (e[e[now].son[0]].sum < x && x <= used) {
    139             splay(now, root);
    140             return e[now].v;
    141         }
    142         if (x < used) {//小于则向左子树寻找 
    143             now = e[now].son[0];
    144         } else {//大于则减去该值,再向右子树寻找 
    145             x -= used;
    146             now = e[now].son[1];
    147         }
    148     }
    149 }
    150 int lower(int v) {//前驱 
    151     int now = root;
    152     int ans = -INF;
    153     while (now) {
    154         if (e[now].v < v && e[now].v > ans) {
    155             ans = e[now].v;
    156         }
    157         if (v > e[now].v) {
    158             now = e[now].son[1];
    159         } else {
    160             now = e[now].son[0];
    161         }
    162     }
    163     return ans;
    164 }
    165 int upper(int v) {//后继 
    166     int now = root;
    167     int ans = INF;
    168     while(now) {
    169         if (e[now].v > v && e[now].v < ans) {
    170             ans = e[now].v;
    171         }
    172         if (v < e[now].v) {
    173             now = e[now].son[0];
    174         } else {
    175             now = e[now].son[1];
    176         } 
    177     }
    178     return ans;
    179 }
    180 int main () {
    181     n = read();
    182     while (n--) {
    183         int o = read();
    184         int x = read();
    185         if (o == 1) {
    186             Insert(x);
    187         } else if (o == 2) {
    188             delet(x);
    189         } else if (o == 3) {
    190             printf("%d
    ", _rank(x));
    191         } else if (o == 4) {
    192             printf("%d
    ", arank(x));
    193         } else if (o == 5) {
    194             printf("%d
    ", lower(x));
    195         } else if (o == 6) {
    196             printf("%d
    ", upper(x));
    197         }
    198     }
    199     return 0;
    200 }
    View Code
  • 相关阅读:
    用Processing生成屏保(二)
    NTFS文件系统
    用processing生成屏保程序
    用processing画李萨如曲线
    processing模拟三角级数合成方波过程
    express 设置 cookie 以及 httpOnly
    vue 使用 axios 时 post 请求方法传参无法发送至后台
    微信小程序访问后台出现 对应的服务器证书无效。控制台输入 showRequestInfo() 可以获取更详细信息。
    微信长按识别二维码,在 vue 项目中的实现
    vue-cli 构建的 Vue 项目用 localhost 加 端口 能访问,但是切换到 ip 加 端口 就不能访问
  • 原文地址:https://www.cnblogs.com/Sundial/p/11830461.html
Copyright © 2011-2022 走看看