zoukankan      html  css  js  c++  java
  • 一天一道算法题——hdu1890 Robotic Sort(splay树)

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=1890

    这题大佬都说是splay树的水题,作为一个新手,苦苦钻研了一天半……我好菜o(╥﹏╥)o

    splay树本质上是一棵BST,它会根据需要把最常用的节点旋转到根节点(或者比它更浅的节点),提高效率。所以它是弱平衡树,不需要计算平衡因子和染色,所以维护成本比较低。

    关键的旋转代码:

     1 int n, root, pre[maxn], child[maxn][2];
     2 void rotate(int x, int d) {//d为0左旋,1右旋
     3     int y = pre[x];
     4     //以下就是把父节点和旋转方向的孩子交换
     5     child[y][!d] = child[x][d];
     6     pre[child[x][d]] = y;
     7     if (pre[y])//如果父节点有父节点,别忘了把自己换上去
     8         child[pre[y]][child[pre[y]][1] == y] = x;
     9     pre[x] = pre[y];
    10     child[x][d] = y;
    11     pre[y] = x;
    12 }
    13 void splay(int r, int goal) {//r是要旋转的节点,goal是要旋转到的地方的父亲
    14     while (pre[r] != goal) {
    15         if (pre[pre[r]] == goal) {
    16             rotate(r, child[pre[r]][0] == r);
    17         }
    18         else {
    19             int y = pre[r];
    20             int d = child[pre[y]][0] == y;
    21             if (child[y][d] == r)//不共线
    22                 rotate(r, !d);
    23             else
    24                 rotate(y, d);
    25             rotate(r, d);
    26         }
    27     }
    28     if (goal == 0)
    29         root = r;//更新根节点
    30 }

    介绍完基本概念,让我们直接进入正题。

    题意是要我们找前i次翻转前的第i大的数的位置。如果直接for,时间复杂度O(n^3),n有10^5,肯定会超时。所以就用到了我们的splay算法,他可以方便的将某个节点给splay到根节点,然后它的左孩子的个数+1就是他的结果了。不过据说不删除已经找过的根节点那么就会超时,所以我们要在输出后删除该根节点,同时输出结果应为他的左孩子个数+i。

    另外,题目要求我们翻转,但翻转会破坏splay树的有序结构,所以这道题其实不算是严格意义上的splay树,我们只是利用了它的splay而已。ㄟ( ▔, ▔ )ㄏ

    以下是AC代码(根据算法竞赛入门进阶——罗勇军著改编):

      1 #include<iostream>
      2 #include<algorithm>
      3 #define maxn 100010
      4 using namespace std;
      5 
      6 int n, root,len[maxn], pre[maxn], child[maxn][2],rev[maxn];//rev是标记——表示是否翻转过,如果每次都翻转那么会超时
      7 struct node {
      8     int val, id;
      9     bool const operator <(node &a)const{
     10         if (val == a.val)return id < a.id;
     11         return val < a.val;
     12     }
     13 }nodes[maxn];
     14 
     15 void pushUp(int x) {//计算长度,不知道为什么要叫pushUp,明明在treap和bst里还叫update的( ・◇・)?
     16     len[x] = len[child[x][0]] + len[child[x][1]]+1;
     17 }
     18 void update_rev(int x) {
     19     if (!x)return;
     20     swap(child[x][0], child[x][1]);
     21     rev[x] ^= 1;
     22 }
     23 void push_down(int x) {
     24     if (rev[x]) {//如果这个节点翻转过,那么把它的孩子节点也需要翻转。
     25         update_rev(child[x][0]);
     26         update_rev(child[x][1]);
     27         rev[x] = 0;
     28     }
     29 }
     30 void rotate(int x, int d) {//d为0左旋,1右旋
     31     int y = pre[x];
     32     push_down(y);
     33     push_down(x);
     34     //以下就是把父节点和旋转方向的孩子交换
     35     child[y][!d] = child[x][d];
     36     pre[child[x][d]] = y;
     37     if (pre[y])//如果父节点有父节点,别忘了把自己换上去
     38         child[pre[y]][child[pre[y]][1] == y] = x;
     39     pre[x] = pre[y];
     40     child[x][d] = y;
     41     pre[y] = x;
     42     pushUp(y);
     43 }
     44 void splay(int r, int goal) {//r是要旋转的节点,goal是要旋转到的地方的父亲
     45     push_down(r);
     46     while (pre[r] != goal) {
     47         if (pre[pre[r]] == goal) {
     48             push_down(pre[r]);
     49             push_down(r);
     50             rotate(r, child[pre[r]][0] == r);
     51         }
     52         else {
     53             push_down(pre[pre[r]]);
     54             push_down(pre[r]);
     55             push_down(r);
     56             int y = pre[r];
     57             int d = child[pre[y]][0] == y;
     58             if (child[y][d] == r)//不共线
     59                 rotate(r, !d);
     60             else
     61                 rotate(y, d);
     62             rotate(r, d);
     63         }
     64     }
     65     pushUp(r);
     66     if (goal == 0)
     67         root = r;
     68 }
     69 void newNode(int &x, int f, int v) {//int &x会直接把传入的x给修改了,这样就不需要重复再给father的左右孩子赋值了
     70     x = v;
     71     len[x] = 1;
     72     pre[x] = f;
     73     rev[x] = 0;
     74     child[x][0] = child[x][1] = 0;
     75 }
     76 void buildTree(int &x, int l, int r, int f) {
     77     //二分保证深度最小
     78     if (l > r)return;
     79     int mid = (l + r) >> 1;
     80     newNode(x,f,mid);
     81     buildTree(child[x][0], l, mid - 1,x);
     82     buildTree(child[x][1], mid + 1, r, x);
     83     pushUp(x);
     84 }
     85 void init() {
     86     root = 0;
     87     child[root][0] = child[root][1] = pre[root] = len[root] = 0;
     88     buildTree(root, 1, n, 0);//以【1,n】为序列建造一棵splaytree
     89 }
     90 void remove(int &x) {
     91     int r = child[x][0];
     92     if (!r) {//没有左孩子
     93         x = child[x][1];
     94         pre[root] = 0;
     95     }
     96     else {
     97         push_down(r);
     98         while(child[r][1]){
     99             r = child[r][1];
    100             push_down(r);
    101         }
    102         splay(r, root);
    103         child[r][1] = child[x][1];
    104         pre[child[x][1]] = r;
    105         x = r;
    106         pre[x] = 0;
    107         pushUp(x);
    108     }
    109 }
    110 int main() {
    111     while (cin >> n && n) {
    112         init();
    113         for (int i = 1; i <= n; i++) {
    114             scanf_s("%d", &nodes[i].val); nodes[i].id = i;//记住位置
    115         }
    116         sort(nodes + 1, nodes + n + 1);
    117         for (int i = 1; i < n; i++) {
    118             splay(nodes[i].id, 0);//将第i小的节点splay到根节点(父节点为0)
    119             update_rev(child[root][0]);
    120             printf("%d ", len[child[root][0]]+i);
    121             remove(root);//删除根节点
    122         }
    123         printf_s("%d
    ", n);//易得
    124     }
    125     return 0;
    126 }
    View Code

    如果感觉自己懂了,可以做一下http://acm.hdu.edu.cn/showproblem.php?pid=4453,这也是splay基本题。

  • 相关阅读:
    LayoutInflater作用及使用--自定义EditText,自带清除内容按钮
    SSL backend error when using OpenSSL pycurl install error
    pypyodbc 的坑
    mongo aggregate 删除重复数据
    如何验证代理ip的正确性
    python的非官方的一个下载lib的地方
    rabbitmq web 管理系统的信息
    内存泄漏分析
    readme 语法
    logging dictconfig
  • 原文地址:https://www.cnblogs.com/zyyz1126/p/12586960.html
Copyright © 2011-2022 走看看