treap启发式合并

注意输入v要在建根的前面。

  1 #include <cstdio>
  2 #include <iostream>
  3 #include <algorithm>
  4 #include <cstring>
  5 #include <cmath>
  6 #include <ctime>
  7 #include <queue>
  8 using namespace std;
  9 const int maxn = 100000 + 10;
 10 int tot = 0, f[maxn], n, Q, v[maxn];
 11 char cmd;
 12 struct Node{
 13     int r, v, s;
 14     Node* ch[2];
 15     void maintain(){
 16         s = ch[0] -> s + ch[1] -> s + 1;
 17         return ;
 18     }
 19 }*null = new Node(), *root[maxn], nodes[maxn];
 20 queue<Node*> RAM;
 21 Node* node(){
 22     Node* o;
 23     if(!RAM.empty()) o = RAM.front(), RAM.pop();
 24     else o = &nodes[tot ++];
 25     return o;
 26 }
 27 void del(Node* &o){
 28     RAM.push(o);
 29     o = null;
 30     return ;
 31 }
 32 void init(Node* &o, int v){
 33     o -> ch[0] = o -> ch[1] = null;
 34     o -> s = 1;
 35     o -> r = rand();
 36     o -> v = v;
 37     return ;
 38 }
 39 void rotate(Node* &o, int d){
 40     Node* k = o -> ch[d ^ 1]; o -> ch[d ^ 1] = k -> ch[d]; k -> ch[d] = o;
 41     o -> maintain(); k -> maintain(); o = k; return ;
 42 }
 43 void insert(Node* &o, int v){
 44     if(o == null) o = node(), init(o, v);
 45     else{
 46         int d = v > o -> v;
 47         insert(o -> ch[d], v);
 48         if(o -> ch[d] -> r > o -> r) rotate(o, d ^ 1);
 49         else o -> maintain();
 50     }
 51     return ;
 52 }
 53 void remove(Node* &o, int v){
 54     if(v == o -> v){
 55         if(o -> ch[0] != null && o -> ch[1] != null){
 56             int d = o -> ch[0] -> r > o -> ch[1] -> r;
 57             rotate(o, d); remove(o -> ch[d], v);
 58         }
 59         else{
 60             Node* k = o;
 61             if(o -> ch[0] != null) o = o -> ch[0];
 62             else o = o -> ch[1];
 63             del(k);
 64         }
 65     }
 66     else remove(o -> ch[v > o -> v], v);
 67     if(o != null) o -> maintain();
 68     return ;
 69 }
 70 void print(Node* &o){
 71     if(o == null) return ;
 72     print(o -> ch[0]);
 73     printf("%d ", o -> v);
 74     print(o -> ch[1]);
 75     return ;
 76 }
 77 int kth(Node* &o, int k){
 78     if(o -> s < k || k < 1) return -1;
 79     if(o -> ch[0] -> s + 1 == k) return o -> v;
 80     if(o -> ch[0] -> s >= k) return kth(o -> ch[0], k);
 81     return kth(o -> ch[1], k - o -> ch[0] -> s - 1);
 82 }
 83 void merge(Node* &left, Node* &right){
 84     if(left == null) return ;
 85     merge(left -> ch[0], right);
 86     merge(left -> ch[1], right);
 87     insert(right, left -> v);
 88     del(left); return ;
 89 }
 90 int findset(int x){
 91     return x == f[x] ? x : f[x] = findset(f[x]);
 92 }
 93 void merge(int a, int b){
 94     a = findset(a); b = findset(b);
 95     if(a == b) return ;
 96     if(root[a] -> s > root[b] -> s) swap(a, b);
 97     merge(root[a], root[b]);
 98     f[a] = b; return ;
 99 }
100 void read(int &x){
101     x = 0; int sig = 1; char ch = getchar();
102     while(!isdigit(ch)) { if(ch == ‘-‘) sig = -1; ch = getchar(); }
103     while(isdigit(ch)) x = 10 * x + ch - ‘0‘, ch = getchar();
104     x *= sig; return ;
105 }
106 void init(){
107     srand(time(0));
108     null -> s = 0;
109     read(n); read(Q);
110     for(int i = 1; i <= n; i ++) read(v[i]);
111     for(int i = 1; i <= n; i ++) f[i] = i, root[i] = node(), init(root[i], v[i]);
112     return ;
113 }
114 void work(){
115
116     return ;
117 }
118 void print(){
119
120     return ;
121 }
122 int main(){
123     init();
124     work();
125     print();
126     return 0;
127 }
时间: 2024-10-10 05:40:37

treap启发式合并的相关文章

BZOJ 2733: [HNOI2012]永无乡(treap + 启发式合并 + 并查集)

不难...treap + 启发式合并 + 并查集 搞搞就行了 ---------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #define rep(i, n) for(int i = 0; i &l

BZOJ 2809 APIO2012 dispatching Treap+启发式合并 / 可并堆

题目大意:给定一棵树,选定一棵子树中的一些点,薪水和不能超过m,求点的数量*子树根节点的领导能力的最大值 考虑对于每个节点,我们维护一种数据结构,在其中贪心寻找薪金小的雇佣. 每个节点暴力重建一定不行,我们考虑可并数据结构,每个节点将子节点的信息直接合并即可 可以用启发式合并的Treap,也可以用可并堆 今天特意去学了这玩应0.0 先写了左偏树 然后又写了下随机堆-- 后者速度上更快一些 不过建议从左偏树开始学起 总之平衡树常数各种大就是了0.0 Treap+启发式合并 #include<cst

BZOJ 2733 HNOI2012 永无乡 Treap+启发式合并

题目大意:给定一个无向图以及n个点的排名,多次连接一条边,多次求某个点所在联通块中排名第k小的点的编号 初始对于每个点建立一棵只有一个节点的Treap,然后每次连接两个点,利用并查集找到两个点的根节点,将size较小的Treap暴力拆解插入大的中,然后将小的并查集合并到大的中 今天下午各种脑残,一个小小的Treap改了不下10遍0.0 快去喝脑白金0.0 #include<cstdio> #include<cstring> #include<iostream> #inc

BZOJ 2733 [HNOI2012]永无乡(启发式合并+Treap+并查集)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2733 [题目大意] 给出n个点,每个点都有自己的重要度,现在有连边操作和查询操作, 查询操作要求找出一个连通块中重要度第k的点的id [题解] 我们用Treap维护每个连通块,对于连边操作,我们用启发式合并, 将size比较小的Treap并入size比较大的Treap,同时用并查集维护连通信息 [代码] #include <cstdio> #include <algorith

启发式合并&amp;线段树合并&amp;treap合并&amp;splay合并

启发式合并 有\(n\)个集合,每次让你合并两个集合,或询问一个集合中是否存在某个元素. ? 我们可以用平衡树/set维护集合. ? 对于合并两个\(A,B\),如果\(|A|<|B|\),那么我们就把\(A\)中的每个元素暴力加到\(B\)中,否则就把\(B\)中的元素暴力加到\(A\)中. ? 对于一次把\(A\)中的每个元素暴力加到\(B\)中的操作,\(|A|\)会变成\(|A|+|B|\),也就是说大小至少会翻倍,所以一个元素最多被暴力插入\(\log n\)次.每次插入的时间复杂度是

启发式合并(堆、set、splay、treap)/线段树合并学习小记

启发式合并 刚听到这个东西的时候,我是相当蒙圈的.特别是"启发式"这三个字莫名的装逼,因此之前一直没有学. 实际上,这个东西就是一个SB贪心. 以堆为例,若我们要合并两个堆a.b,我们有一种极其简单的做法:那就是比较一下它们的大小,将小的堆的每个元素依次插入到大的堆中.不妨设\(|a|≤|b|\),则时间复杂度即为:\(O(|a|*log_2(|a|+|b|))\). 这个东西看似很慢,但当点数较小的时候,我们可以证明复杂度是可被接受的. 比如我们要合并n个堆,这n个堆共有m个点.设这

Bzoj 2733: [HNOI2012]永无乡 数组Splay+启发式合并

2733: [HNOI2012]永无乡 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 3955  Solved: 2112[Submit][Status][Discuss] Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达

BZOJ 2809 APIO 2012 dispatching 平衡树启发式合并

题目大意:给出一棵树,每一个节点有两个值,分别是这个忍者的薪水和忍者的领导力.客户的满意程度是这个点的领导力乘能够取得人数,前提是取的人的薪水总和不超过总的钱数. 思路:只能在子树中操作,贪心的想,我们只要这个子树中cost最小的那些点就可以了.所以就深搜一次,每到一个节点上,把自己和所有子节点的平衡树启发式和并,然后保留不超过总钱数的人数,统计.数据范围比较大,能开long long的地方不要吝啬. PS:吐槽一下,一开始这个题一直TTT,我以为是我常数写的太大了,别人都用左偏堆写,是不是平衡

【Splay】【启发式合并】hdu6133 Army Formations

题意:给你一颗树,每个结点的儿子数不超过2.每个结点有一个权值,一个结点的代价被定义为将其子树中所有结点的权值放入数组排序后,每个权值乘以其下标的和.让你计算所有结点的代价. 二叉树的条件没有用到. 每个结点开一个Splay,从叶子往上启发式合并上去,可以先bfs一遍确定合并顺序.每一次将Splay大小较小的结点的权值全提取出来塞到较大的里面. 由于权值可能重复出现,所以每个结点记个cnt. 答案统计的时候,就将那个刚塞进去的旋到根,然后答案加上左子树的权值和,再加上(右子树的权值的个数+该结点