$fhqTreap$

Posted colythme

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了$fhqTreap$相关的知识,希望对你有一定的参考价值。

- $fhqTreap$与$Treap$的差异

  $fhqTreap$是$Treap$的非旋版本,可以实现一切$Treap$操作,及区间操作和可持久化

  $fhqTreap$非旋关键在于分裂与合并$(Split & Merge)$

- $Split$

  分裂相当于将一棵平衡树分为两棵平衡树,比如可以以值$x$为分界线,即分为一棵权值均$le x$的平衡树,及一棵权值均$> x$的平衡树
  对于实现,有$A$树根$rtA$及$B$树根$rtB$,若当前结点$root$权值$le x$,则$root$左子树均属于$A$树,$>$同理

void Split (int root, int mid, int& rtA, int& rtB) {
    if (! root) {
        rtA = rtB = 0;
        return ;
    }
    if (Val[root] <= mid)
        rtA = root, Split (Son[root][1], mid, Son[root][1], rtB);
    else
        rtB = root, Split (Son[root][0], mid, rtA, Son[root][0]);
    update (root);
}

 

- $Merge$

  将两棵树合并为一棵,需要既满足$BST$亦满足$Heap$,又$A$树的权值均小于$B$树,所以$BST$性质很好满足,那么再判一下随机的$rank$大小就好了
如果$A$树结点$node_a$的$rank$小于$B$树的结点$node_b$的$rank$,那么$node_b$必然是$node_a$的右子节点,反之$node_a$必然是$node_b$的左子结点

int Merge (int rtA, int rtB) {
    if (! rtA || ! rtB)
        return rtA + rtB;
    if (Rank[rtA] < Rank[rtB]) {
        Son[rtA][1] = Merge (Son[rtA][1], rtB);
        update (rtA);
        return rtA;
    }
    else {
        Son[rtB][0] = Merge (rtA, Son[rtB][0]);
        update (rtB);
        return rtB;
    }
}

 

- 单点操作

  - $Insert$
    将树以插入结点权值$x$分裂,在权值均$le x$的$A$树中插入即可

void Insert (int x) {
  int rtA, rtB;
  Split (Root, x, rtA, rtB);
  Root = Merge (Merge (rtA, newnode (x)), rtB);
}


  - $Delete$
    将树以$x$分裂为$A$与$B$,再将$A$以$x - 1$为基准继续分裂为$C$与$D$,那么将$D$根节点的左右子节点合并即可

void Delete (int x) {
  int rtA, rtB, rtC;
  Split (Root, x, rtA, rtB);
  Split (rtA, x - 1, rtA, rtC);
  rtC = Merge (Son[rtC][0], Son[rtC][1]);
  Root = Merge (Merge (rtA, rtC), rtB);
}


  - $Query\_Rank$

int Query_Rank (int x) {
    int rtA, rtB;
    Split (Root, x - 1, rtA, rtB);
    int rank = Size[rtA] + 1;
      Root = Merge (rtA, rtB);
      return rank;
}


  - $Query\_Kth$

int Query_Kth (int root, int k) {
    while (true) {
        if (Size[Son[root][0]] >= k)
            root = Son[root][0];
        else {
            k -= Size[Son[root][0]] + 1;
            if (! k)
                return Val[root];
            root = Son[root][1];
        }
    }
}


  - 前驱结点
    将树以$x - 1$分裂,那么树$A$的最后一名即为该结点的前驱结点

int Query_Prenode (int x) {
    int rtA, rtB;
    Split (Root, x - 1, rtA, rtB);
    int pre = Query_Kth (rtA, Size[rtA]);
    Root = Merge (rtA, rtB);
    return pre;
}


  - 后继结点

int Query_Nextnode (int x) {
    int rtA, rtB;
    Split (Root, x, rtA, rtB);
    int nxt = Query_Kth (rtB, 1);
    Root = Merge (rtA, rtB);
    return nxt;
}

  - 完整代码

技术分享图片
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <ctime>
  5 #include <algorithm>
  6 
  7 using namespace std;
  8 
  9 const int MAXN = 1e05 + 10;
 10 
 11 int Son[MAXN][2]= {0};
 12 int Size[MAXN]= {0};
 13 int Val[MAXN]= {0};
 14 int Rank[MAXN]= {0};
 15 int nodes = 0;
 16 
 17 int Root = 0;
 18 int newnode (int val) {
 19     int root = ++ nodes;
 20     Size[root] = 1;
 21     Val[root] = val;
 22     Rank[root] = rand ();
 23     return root;
 24 }
 25 
 26 void update (int root) {
 27     Size[root] = Size[Son[root][0]] + Size[Son[root][1]] + 1;
 28 }
 29 
 30 void Split (int root, int mid, int& rtA, int& rtB) {
 31     if (! root) {
 32         rtA = rtB = 0;
 33         return ;
 34     }
 35     if (Val[root] <= mid)
 36         rtA = root, Split (Son[root][1], mid, Son[root][1], rtB);
 37     else
 38         rtB = root, Split (Son[root][0], mid, rtA, Son[root][0]);
 39     update (root);
 40 }
 41 
 42 int Merge (int rtA, int rtB) {
 43     if (! rtA || ! rtB)
 44         return rtA + rtB;
 45     if (Rank[rtA] < Rank[rtB]) {
 46         Son[rtA][1] = Merge (Son[rtA][1], rtB);
 47         update (rtA);
 48         return rtA;
 49     }
 50     else {
 51         Son[rtB][0] = Merge (rtA, Son[rtB][0]);
 52         update (rtB);
 53         return rtB;
 54     }
 55 }
 56 
 57 void Insert (int x) {
 58     int rtA, rtB;
 59     Split (Root, x, rtA, rtB);
 60     Root = Merge (Merge (rtA, newnode (x)), rtB);
 61 }
 62 
 63 void Delete (int x) {
 64     int rtA, rtB, rtC;
 65     Split (Root, x, rtA, rtB);
 66     Split (rtA, x - 1, rtA, rtC);
 67     rtC = Merge (Son[rtC][0], Son[rtC][1]);
 68     Root = Merge (Merge (rtA, rtC), rtB);
 69 }
 70 
 71 int Query_Rank (int x) {
 72     int rtA, rtB;
 73     Split (Root, x - 1, rtA, rtB);
 74     int rank = Size[rtA] + 1;
 75     Root = Merge (rtA, rtB);
 76     return rank;
 77 }
 78 
 79 int Query_Kth (int root, int k) {
 80     while (true) {
 81         if (Size[Son[root][0]] >= k)
 82             root = Son[root][0];
 83         else {
 84             k -= Size[Son[root][0]] + 1;
 85             if (! k)
 86                 return Val[root];
 87             root = Son[root][1];
 88         }
 89     }
 90 }
 91 
 92 int Query_Prenode (int x) {
 93     int rtA, rtB;
 94     Split (Root, x - 1, rtA, rtB);
 95     int pre = Query_Kth (rtA, Size[rtA]);
 96     Root = Merge (rtA, rtB);
 97     return pre;
 98 }
 99 
100 int Query_Nextnode (int x) {
101     int rtA, rtB;
102     Split (Root, x, rtA, rtB);
103     int nxt = Query_Kth (rtB, 1);
104     Root = Merge (rtA, rtB);
105     return nxt;
106 }
107 
108 int M;
109 
110 int getnum () {
111     int num = 0;
112     char ch = getchar ();
113     int flag = 0;
114 
115     while (! isdigit (ch)) {
116         if (ch == -)
117             flag = 1;
118         ch = getchar ();
119     }
120     while (isdigit (ch))
121         num = (num << 3) + (num << 1) + ch - 0, ch = getchar ();
122 
123     return flag ? - num : num;
124 }
125 
126 int main () {
127     // freopen ("Input.txt", "r", stdin);
128 
129     srand (time (NULL));
130 
131     M = getnum ();
132     for (int Case = 1; Case <= M; Case ++) {
133         int opt = getnum ();
134         int x, k;
135         int rank, pre, nxt;
136         switch (opt) {
137             case 1:
138                 x = getnum ();
139                 Insert (x);
140                 break;
141             case 2:
142                 x = getnum ();
143                 Delete (x);
144                 break;
145             case 3:
146                 x = getnum ();
147                 rank = Query_Rank (x);
148                 printf ("%d
", rank);
149                 break;
150             case 4:
151                 k = getnum ();
152                 x = Query_Kth (Root, k);
153                 printf ("%d
", x);
154                 break;
155             case 5:
156                 x = getnum ();
157                 pre = Query_Prenode (x);
158                 printf ("%d
", pre);
159                 break;
160             case 6:
161                 x = getnum ();
162                 nxt = Query_Nextnode (x);
163                 printf ("%d
", nxt);
164                 break;
165         }
166     }
167 
168     return 0;
169 }
170 
171 /*
172 10
173 1 106465
174 4 1
175 1 317721
176 1 460929
177 1 644985
178 1 84185
179 1 89851
180 6 81968
181 1 492737
182 5 493598
183 */
fhqTreap

 

- 区间操作

  - 建树
    类似线段树的建法即可

int Build (int left, int right) {
    if (left > right)
        return 0;
    int mid = (left + right) >> 1;
    int root = newnode (mid - 1);
    Son[root][0] = Build (left, mid - 1);
    Son[root][1] = Build (mid + 1, right);
    update (root);
    return root;
}


  - 区间操作
    将树以$R$分裂,再将$A$树以$L - 1$分裂,那么得到的树$D$,就恰好包含了区间$[L, R]$,直接操作即可

int rtA, rtB;
int rtC, rtD;
Split (Root, R, rtA, rtB);
Split (rtA, L - 1, rtC, rtD);
// 以下为操作

  - 完整代码  

技术分享图片
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <ctime>
  5 #include <algorithm>
  6 
  7 using namespace std;
  8 
  9 const int MAXN = 1e05 + 10;
 10 
 11 int Son[MAXN][2]= {0};
 12 int Size[MAXN]= {0};
 13 int Val[MAXN]= {0};
 14 int Rank[MAXN]= {0};
 15 int Revtag[MAXN]= {0};
 16 int nodes = 0;
 17 
 18 int Root = 0;
 19 int newnode (int x) {
 20     int root = ++ nodes;
 21     Size[root] = 1;
 22     Val[root] = x;
 23     Rank[root] = rand ();
 24     Revtag[root] = 0;
 25     return root;
 26 }
 27 
 28 void update (int root) {
 29     Size[root] = Size[Son[root][0]] + Size[Son[root][1]] + 1;
 30 }
 31 
 32 int Build (int left, int right) {
 33     if (left > right)
 34         return 0;
 35     int mid = (left + right) >> 1;
 36     int root = newnode (mid - 1);
 37     Son[root][0] = Build (left, mid - 1);
 38     Son[root][1] = Build (mid + 1, right);
 39     update (root);
 40     return root;
 41 }
 42 
 43 void pushdown (int root) {
 44     if (Revtag[root]) {
 45         swap (Son[root][0], Son[root][1]);
 46         Revtag[Son[root][0]] ^= 1;
 47         Revtag[Son[root][1]] ^= 1;
 48         Revtag[root] = 0;
 49     }
 50 }
 51 
 52 void Split (int root, int mid, int& rtA, int& rtB) {
 53     if (! root) {
 54         rtA = rtB = 0;
 55         return ;
 56     }
 57     pushdown (root);
 58     if (Size[Son[root][0]] >= mid)
 59         rtB = root, Split (Son[root][0], mid, rtA, Son[root][0]);
 60     else
 61         rtA = root, Split (Son[root][1], mid - Size[Son[root][0]] - 1, Son[root][1], rtB);
 62     update (root);
 63 }
 64 
 65 int Merge (int rtA, int rtB) {
 66     if (! rtA || ! rtB)
 67         return rtA + rtB;
 68     pushdown (rtA), pushdown (rtB);
 69     if (Rank[rtA] < Rank[rtB]) {
 70         Son[rtA][1] = Merge (Son[rtA][1], rtB);
 71         update (rtA);
 72         return rtA;
 73     }
 74     else {
 75         Son[rtB][0] = Merge (rtA, Son[rtB][0]);
 76         update (rtB);
 77         return rtB;
 78     }
 79 }
 80 
 81 void Reverse (int L, int R) {
 82     int rtA, rtB;
 83     int rtC, rtD;
 84     Split (Root, R, rtA, rtB);
 85     Split (rtA, L - 1, rtC, rtD);
 86     Revtag[rtD] ^= 1;
 87     rtA = Merge (rtC, rtD);
 88     Root = Merge (rtA, rtB);
 89 }
 90 
 91 int N, M;
 92 
 93 void Output (int root) {
 94     pushdown (root);
 95     if (Son[root][0])
 96         Output (Son[root][0]);
 97     if (Val[root] >= 1 && Val[root] <= N)
 98         printf ("%d ", Val[root]);
 99     if (Son[root][1])
100         Output (Son[root][1]);
101 }
102 
103 int getnum () {
104     int num = 0;
105     char ch = getchar ();
106 
107     while (! isdigit (ch))
108         ch = getchar ();
109     while (isdigit (ch))
110         num = (num << 3) + (num << 1) + ch - 0, ch = getchar ();
111 
112     return num;
113 }
114 
115 int main () {
116     srand (time (NULL));
117     N = getnum (), M = getnum ();
118     Root = Build (1, N + 2);
119     for (int Case = 1; Case <= M; Case ++) {
120         int l = getnum (), r = getnum ();
121         l ++, r ++;
122         Reverse (l, r);
123     }
124     Output (Root);
125     puts ("");
126 
127     return 0;
128 }
129 
130 /*
131 5 1
132 1 3
133 */
134 
135 /*
136 5 3
137 1 3
138 1 3
139 1 4
140 */
区间翻转操作

 

- 可持久化

  直接类似一般的可持久化实现,主要修改在$Split$与$Merge$操作

技术分享图片
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <ctime>
  5 #include <algorithm>
  6 
  7 using namespace std;
  8 
  9 const int MAXN = 5e05 + 10;
 10 const int MAXLOG = 40 + 10;
 11 
 12 int Son[MAXN * MAXLOG][2]= {0};
 13 int Size[MAXN * MAXLOG]= {0};
 14 int Val[MAXN * MAXLOG]= {0};
 15 int Rank[MAXN * MAXLOG]= {0};
 16 int nodes = 0;
 17 
 18 int Root[MAXN]= {0};
 19 int newnode (int val) {
 20     int root = ++ nodes;
 21     Size[root] = 1;
 22     Val[root] = val;
 23     Rank[root] = rand ();
 24     return root;
 25 }
 26 
 27 void update (int root) {
 28     Size[root] = Size[Son[root][0]] + Size[Son[root][1]] + 1;
 29 }
 30 
 31 void copy (int pre, int p) {
 32     Son[p][0] = Son[pre][0], Son[p][1] = Son[pre][1];
 33     Size[p] = Size[pre];
 34     Val[p] = Val[pre];
 35     Rank[p] = Rank[pre];
 36 }
 37 
 38 void Split (int pre, int mid, int& rtA, int& rtB) {
 39     if (! pre) {
 40         rtA = rtB = 0;
 41         return ;
 42     }
 43     if (Val[pre] <= mid) {
 44         rtA = ++ nodes;
 45         copy (pre, rtA);
 46         Split (Son[pre][1], mid, Son[rtA][1], rtB);
 47         update (rtA);
 48     }
 49     else {
 50         rtB = ++ nodes;
 51         copy (pre, rtB);
 52         Split (Son[pre][0], mid, rtA, Son[rtB][0]);
 53         update (rtB);
 54     }
 55 }
 56 
 57 int Merge (int prertA, int prertB) {
 58     if (! prertA || ! prertB)
 59         return prertA + prertB;
 60     if (Rank[prertA] < Rank[prertB]) {
 61         int rtA = ++ nodes;
 62         copy (prertA, rtA);
 63         Son[rtA][1] = Merge (Son[prertA][1], prertB);
 64         update (rtA);
 65         return rtA;
 66     }
 67     else {
 68         int rtB = ++ nodes;
 69         copy (prertB, rtB);
 70         Son[rtB][0] = Merge (prertA, Son[prertB][0]);
 71         update (rtB);
 72         return rtB;
 73     }
 74 }
 75 
 76 void Insert (int& proot, int x) {
 77     int rtA, rtB;
 78     Split (proot, x, rtA, rtB);
 79     proot = Merge (Merge (rtA, newnode (x)), rtB);
 80 }
 81 
 82 void Delete (int& proot, int x) {
 83     int rtA, rtB, rtC;
 84     Split (proot, x, rtA, rtB);
 85     Split (rtA, x - 1, rtA, rtC);
 86     rtC = Merge (Son[rtC][0], Son[rtC][1]);
 87     proot = Merge (Merge (rtA, rtC), rtB);
 88 }
 89 
 90 int Query_Rank (int& proot, int x) {
 91     int rtA, rtB;
 92     Split (proot, x - 1, rtA, rtB);
 93     int rank = Size[rtA] + 1;
 94     proot = Merge (rtA, rtB);
 95     return rank;
 96 }
 97 
 98 int Query_Kth (int root, int k) {
 99     while (true) {
100         if (Size[Son[root][0]] >= k)
101             root = Son[root][0];
102         else {
103             k -= Size[Son[root][0]] + 1;
104             if (! k)
105                 return Val[root];
106             root = Son[root][1];
107         }
108     }
109 }
110 
111 int Query_Prenode (int& proot, int x) {
112     int rtA = 0, rtB;
113     Split (proot, x - 1, rtA, rtB);
114     if (! rtA)
115         return - 2147483647;
116     int pre = Query_Kth (rtA, Size[rtA]);
117     proot = Merge (rtA, rtB);
118     return pre;
119 }
120 
121 int Query_Nextnode (int& proot, int x) {
122     int rtA, rtB = 0;
123     Split (proot, x, rtA, rtB);
124     if (! rtB)
125         return 2147364847;
126     int nxt = Query_Kth (rtB, 1);
127     proot = Merge (rtA, rtB);
128     return nxt;
129 }
130 
131 int M;
132 
133 int getnum () {
134     int num = 0;
135     char ch = getchar ();
136     int flag = 0;
137 
138     while (! isdigit (ch)) {
139         if (ch == -)
140             flag = 1;
141         ch = getchar ();
142     }
143     while (isdigit (ch))
144         num = (num << 3) + (num << 1) + ch - 0, ch = getchar ();
145 
146     return flag ? - num : num;
147 }
148 
149 int main () {
150     // freopen ("Input.txt", "r", stdin);
151 
152     srand (time (NULL));
153     M = getnum ();
154     for (int Case = 1; Case <= M; Case ++) {
155         Root[Case] = Root[getnum ()];
156         int opt = getnum ();
157         int x, k;
158         int rank, pre, nxt;
159         switch (opt) {
160             case 1:
161                 x = getnum ();
162                 Insert (Root[Case], x);
163                 break;
164             case 2:
165                 x = getnum ();
166                 Delete (Root[Case], x);
167                 break;
168             case 3:
169                 x = getnum ();
170                 rank = Query_Rank (Root[Case], x);
171                 printf ("%d
", rank);
172                 break;
173             case 4:
174                 k = getnum ();
175                 x = Query_Kth (Root[Case], k);
176                 printf ("%d
", x);
177                 break;
178             case 5:
179                 x = getnum ();
180                 pre = Query_Prenode (Root[Case], x);
181                 printf ("%d
", pre);
182                 break;
183             case 6:
184                 x = getnum ();
185                 nxt = Query_Nextnode (Root[Case], x);
186                 printf ("%d
", nxt);
187                 break;
188         }
189     }
190     
191     return 0;
192 }
193 
194 /*
195 10
196 0 1 9
197 1 1 3
198 1 1 10
199 2 4 2
200 3 3 9
201 3 1 2
202 6 4 1
203 6 2 9
204 8 6 3
205 4 5 8
206 */
可持久化平衡树

 

以上是关于$fhqTreap$的主要内容,如果未能解决你的问题,请参考以下文章

fhqtreap入门

fhqTreap

fhqTreap

[模板]fhqTreap

$fhqTreap$

震惊!一蒟蒻竟然写出fhqTreap