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大的方法就出来了。
- 依次插入序列中的数,每插入一个树建立一个新树。
- 若查询区间[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);
}
- 最后是完整代码:链接