Chairman tree notes

Chairman tree notes

  • By BigYellowDog
  • Front cheese: tree line, and prefix, as well as the best balanced tree.

Importing

  • What Chairman tree? It can be doing? Why use it?
  • Chairman of the tree with the name of the function does not matter. Origin of the name is said to be hjt Daniel invention, named according to their screen name.
  • Chairman of the tree real name may persist tree line.
  • Suppose now that there is such a problem: the existing one sequence, q times asking. Each Q interval maximum value [l, r] in the.
  • Very simple ah, segment tree / st table just fine.
  • That change it, ask each interval [l, r] is the second largest in value.
  • Very simple ah, like a tree line, but more just to maintain a value.
  • That change it again, each time asking the interval [l, r] in the k-th largest value.
  • ... ...
  • emmm Anyway, I can not write here.
  • Then the Chairman of the tree is seeking to solve the k-th largest range of such issues.

  • Of course, it can also be the k-th section integral with two points. But the Chairman of the tree is easy to understand, the code is not large. So high is a cost-effective method.

principle

  • Now there is a sequence of 35 412, find the interval [1, 4] second large.
  • ok now Chairman of the tree to simulate the process of the algorithm.

  • Build an empty tree (tree 0), as shown below.

  • Brown the digital representation of the node.

  • (1-5) represented by the node 1 <= value <= number 5
  • (1-3) This node represents 1 <= value <= 3 the number of
  • ... ... Note that a weight segment tree. The point about the endpoint represents the boundary weights.

  • A first inserted sequence number 3, FIG becomes (a tree)

  • A second inserted sequence number 5, FIG becomes (tree 2)

  • After the sequence number has been inserted after the first 4 1, FIG becomes (tree 4)

  • Here come to a halt, we have inserted before the number 4, then you can query the interval [1, 4] of the k big.
  • First entry (1-5) node, the number found to the left subtree son 2 <= k (2), then entry (1-3) Node
  • Found (1-3) the number of the left son node subtree 1 <k (2), then into the (3 3) node, k update which is 2 - 1 = 1
  • Run out, then return to the left node point 3. Thus the interval [1, 4] 3 is the second small
  • Why return to the left endpoint? Because this is a weight segment tree.
  • Above the tree line where to find the weight of the k large tree process is to find a large balance of the k process

  • 那现在,请找出区间[2, 4]里的第2大值。
  • 很容易啊,我们取出刚刚建出的树4和树1。拿树4 减 树1即可得到一个新树。
  • 减,即拿每个对应节点相减。
  • 在这个新树上找第2大即可。
  • 这就是一个前缀和啊。

  • 那利用主席树解决区间第k大的方法就出来了。
  1. 依次插入序列中的数,每插入一个树建立一个新树。
  2. 若查询区间[l, r],则拿出树r和树(l - 1)相减得到新树,在新树上找第k大。

实现

  • 原理就是这样啦,但是如果每插入一个树就建立一颗新树,那岂不是空间爆炸?
  • 是的,原理很简单,关键就是实现,这也是发明者的精妙之处。
  • 首先可以发现一个性质,每插入一个,相对于上一棵来说,只会有从根节点到叶节点的一条链上的点的值发生了变化。其它都是不变的!
  • 如图,这是上述序列插入3后的图,当插入4后只有蓝色路径上的节点的值会++

  • 那我们每建立一颗新树就不用重新建了,而是值改变的点就重建,没改变的连到上一个树上去即可。那么上述的图可以变成

  • 带 ' 的表示是插入4后的,不带 ' 的是插入3后的树。
  • 这样建树的空间开销就大大减少了。

代码

cin >> n >> m;
for(int i = 1; i <= n; i++)
    a[i] = read(), b[++cnt] = a[i];
sort(b + 1, b + 1 + cnt);
cnt = unique(b + 1, b + 1 + cnt) - b - 1;
/**
 * 首先输入序列中的每一个数,因为权值很大,我们又要按照权值建树,那么就先离散化下
 * a是原序列,b是离散化后的数组,cnt是离散化的不同权值个数
*/

r[0] = build(1, cnt); //建一个空树, r[0]表示第0棵树的根节点
int build(int l, int r) //建树函数,各位都懂
{
    int p = ++dex, mid = l + r >> 1;
    if(l == r) return p;
    t[p].l = build(l, mid);
    t[p].r = build(mid + 1, r);
    return p;
}

for(int i = 1; i <= n; i++) //这里就是每插入一个数建一个树的过程了
    r[i] = upd(r[i - 1], 1, cnt, find(a[i]));
int upd(int las, int l, int r, int val)
{
    int p = ++dex, mid = l + r >> 1;
    t[p].l = t[las].l, t[p].r = t[las].r; //首先都给它连上上一棵树的节点
    t[p].sum = t[las].sum + 1;
    if(l == r) return p;
    if(val <= mid) t[p].l = upd(t[las].l, l, mid, val); //说明左子树发生了改变
    else t[p].r = upd(t[las].r, mid + 1, r, val); //说明右子树发生了改变
    return p;
}

for(int i = 1; i <= m; i++)
{
    int ll = read(), rr = read(), rank = read();
    printf("%d\n", b[ask(r[ll - 1], r[rr], 1, cnt, rank)]);
}
int ask(int u, int v, int l, int r, int rank) //取出了树u和树v
{
    if(l == r) return l;
    int size = t[t[v].l].sum - t[t[u].l].sum, mid = l + r >> 1; //得到新树的左儿子子树个数
    if(rank <= size) return ask(t[u].l, t[v].l, l, mid, rank);
    else return ask(t[u].r, t[v].r, mid + 1, r, rank - size);
}
  • 最后是完整代码:链接

练习题

  1. 模板
  2. 练手题
  3. 跑到树上的主席树

Guess you like

Origin www.cnblogs.com/BigYellowDog/p/11776243.html