• 一天一道算法题——Treap树(树堆)


     Treap,由Tree和Heap各取一半组成的单词,也可意为树堆。

    二叉查找树为每一个结点设置了一个键值,Treap树在上面又加了一个被称为优先级的权值。对键值来说,Treap树是排序二叉树;对优先级来说,Treap树是堆——在这棵树的任意子树上,根节点的优先级最大。

    Treap的唯一性指:另每个节点的优先级互不相等,则该棵树的形态唯一,与元素的插入顺序无关。

     上图描述了Treap树的建树过程。

    节点定义:

     1 struct Node
     2 {
     3     int size;
     4     int rank;
     5     int key;
     6     Node *son[2];
     7     bool operator < (const Node &a)const
     8     {
     9         return rank<a.rank;
    10     }
    11     int cmp(int x)const
    12     {
    13         if(x==key) return -1;
    14         return x<key?0:1;
    15     }
    16     void update()
    17     {
    18         size=1+son[0]->size+son[1]->size;
    19     }
    20 };

    首先我们来实现插入操作。

    方法一:已知优先级,按照优先级由大到小排序,然后键值比根节点小的放左子树,大的放右子树。

    方法二:按照BST的插入先将新结点插入到所在位置,然后为该节点分配一个随机的优先级,比较和根节点优先级的大小,如果比根节点大,那么就往上走。

     1 void rotate(Node* &o,int d)
     2     {
     3         Node *k=o->son[d^1];
     4         o->son[d^1]=k->son[d];
     5         k->son[d]=o;
     6         o->update();
     7         k->update();
     8         o=k;
     9     }
    10     void build(Node* &o,int x)
    11     {
    12         if(o==null)
    13         {
    14             o=new Node();
    15             o->son[0]=o->son[1]=null;
    16             o->rank=rand();
    17             o->key=x;
    18             o->size=1;
    19         }
    20         else
    21         {
    22             int d=o->cmp(x);
    23             build(o->son[d],x);
    24             o->update();
    25             if(o<o->son[d])
    26                 rotate(o,d^1);
    27         }
    28     }

    因为我们需要修改o的值,所以在形参里用了*&

    然后是找到x的在Treap树里的排名。所以我们只需要判断比它大的元素有几个就好了。

    int kth(Node* o,int 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);
        }

    接着是找到排名第x的元素。

     1 int find(Node* o,int k)
     2     {
     3         if(o==null) return -1;
     4         int d=o->cmp(k);
     5         if(d==-1) return o->son[1]->size+1;
     6         else if(d==1) return find(o->son[d],k);
     7         else
     8         {
     9             int tmp=find(o->son[d],k);
    10             if(tmp==-1) return -1;
    11             else return tmp+1+o->son[1]->size;
    12         }
    13     }

    然后是删除元素x

     1 void erase(Node *&t, int x) {
     2         if (t == null)return;
     3         if (t->key == x) {
     4             if (t->son[0] == null || t->son[1] == null) {
     5                 Node *p = t;
     6                 if (t->son[0] == null)
     7                     t = t->son[1];
     8                 else
     9                     t = t->son[0];
    10                 delete p;
    11             }
    12             else {
    13                 int d = t->son[0] > t->son[1] ? 1 : 0;
    14                 rotate(t, d ^ 1);
    15                 erase(t->son[d], x);
    16             }
    17         }
    18         else if (x < t->key)erase(t->son[0], x);
    19         else erase(t->son[1], x);
    20 }

    以下为例题。

    HUD4585 Shaolin

    Problem Description
    Shaolin temple is very famous for its Kongfu monks.A lot of young men go to Shaolin temple every year, trying to be a monk there. The master of Shaolin evaluates a young man mainly by his talent on understanding the Buddism scripture, but fighting skill is also taken into account.
    When a young man passes all the tests and is declared a new monk of Shaolin, there will be a fight , as a part of the welcome party. Every monk has an unique id and a unique fighting grade, which are all integers. The new monk must fight with a old monk whose fighting grade is closest to his fighting grade. If there are two old monks satisfying that condition, the new monk will take the one whose fighting grade is less than his.
    The master is the first monk in Shaolin, his id is 1,and his fighting grade is 1,000,000,000.He just lost the fighting records. But he still remembers who joined Shaolin earlier, who joined later. Please recover the fighting records for him.
     
    Input
    There are several test cases.
    In each test case:
    The first line is a integer n (0 <n <=100,000),meaning the number of monks who joined Shaolin after the master did.(The master is not included).Then n lines follow. Each line has two integer k and g, meaning a monk's id and his fighting grade.( 0<= k ,g<=5,000,000)
    The monks are listed by ascending order of jointing time.In other words, monks who joined Shaolin earlier come first.
    The input ends with n = 0
    Output
    A fight can be described as two ids of the monks who make that fight. For each test case, output all fights by the ascending order of happening time. Each fight in a line. For each fight, print the new monk's id first ,then the old monk's id.
     
    Sample Input
    3 2 1 3 3 4 2 0
     
    Sample Output
    2 1 3 2 4 2
      1 #include <iostream>
      2 #include <stdio.h>
      3 #include <string.h>
      4 #include <time.h>
      5 #include <stdlib.h>
      6 #include <math.h>
      7 using namespace std;
      8 int vis[5000000+5];
      9 struct Node
     10 {
     11     int size;
     12     int rank;
     13     int key;
     14     Node *son[2];
     15     bool operator < (const Node &a)const
     16     {
     17         return rank<a.rank;
     18     }
     19     int cmp(int x)const
     20     {
     21         if(x==key) return -1;
     22         return x<key?0:1;
     23     }
     24     void update()
     25     {
     26         size=1+son[0]->size+son[1]->size;
     27     }
     28 };
     29 struct Treap
     30 {
     31     Node *root;
     32     Node *null=new Node();
     33     void init()
     34     {
     35         srand(time(NULL));
     36         root=null;
     37     }
     38     void rotate(Node* &o,int d)
     39     {
     40         Node *k=o->son[d^1];
     41         o->son[d^1]=k->son[d];
     42         k->son[d]=o;
     43         o->update();
     44         k->update();
     45         o=k;
     46     }
     47     void build(Node* &o,int x)
     48     {
     49         if(o==null)
     50         {
     51             o=new Node();
     52             o->son[0]=o->son[1]=null;
     53             o->rank=rand();
     54             o->key=x;
     55             o->size=1;
     56         }
     57         else
     58         {
     59             int d=o->cmp(x);
     60             build(o->son[d],x);
     61             o->update();
     62             if(o<o->son[d])
     63                 rotate(o,d^1);
     64         }
     65     }
     66     int kth(Node* o,int k)
     67     {
     68         if(o==null||k<=0||k>o->size) return -1;
     69         int s=o->son[1]==null?0:o->son[1]->size;
     70         if(k==s+1) return o->key;
     71         else if(k<=s) return kth(o->son[1],k);
     72         else return kth(o->son[0],k-s-1);
     73     }
     74     int find(Node* o,int k)
     75     {
     76         if(o==null) return -1;
     77         int d=o->cmp(k);
     78         if(d==-1) return o->son[1]->size+1;
     79         else if(d==1) return find(o->son[d],k);
     80         else
     81         {
     82             int tmp=find(o->son[d],k);
     83             if(tmp==-1) return -1;
     84             else return tmp+1+o->son[1]->size;
     85         }
     86     }
     87 } treap;
     88 int main()
     89 {
     90     int n;
     91     while(~scanf("%d",&n)&&n)
     92     {
     93         treap.init();
     94         int x,y;
     95         scanf("%d%d",&x,&y);
     96         treap.build(treap.root,y);
     97         vis[y]=x;
     98         printf("%d %d
    ",x,1);
     99         for(int i=2;i<=n;i++)
    100         {
    101             scanf("%d%d",&x,&y);
    102             vis[y]=x;
    103             treap.build(treap.root,y);
    104             int t=treap.find(treap.root,y);
    105             int ans1,ans2,ans;
    106             ans1=treap.kth(treap.root,t-1);
    107             ans2=treap.kth(treap.root,t+1);
    108             if(ans1!=-1&&ans2!=-1)
    109             {
    110                 ans=ans1-y>=y-ans2?ans2:ans1;
    111             }
    112             else if(ans1==-1) ans=ans2;
    113             else ans=ans1;
    114             printf("%d %d
    ",x,vis[ans]);
    115         }
    116     }
    117     return 0;
    118 }

    之后会把hdu3726也加上来。

  • 相关阅读:
    QT编译./configure参数的详细解释
    在pcduino安装Qt
    在ubuntu上安装opengl es2.0 来编译Qt5.2
    Linux 常用命令
    关键字:auto、static、register、const、volatile 、extern 总结
    C++CLI编程(一、命名空间)
    优秀的代码风格
    HTTP web错误
    来自网络的收藏分享
    虚基类的作用
  • 原文地址:https://www.cnblogs.com/zyyz1126/p/12558757.html
Copyright © 2020-2023  润新知