笼统的主席树原理
众所周知, 主席树是可以持久化的, 换言之你能知道你所维护信息的所有历史状态。 主席树是这样做的:
1.
首先建一颗朴素的线段树,代表初始状态 (下图黑色) , 也就是第0次操作后的状态。
tipA:
你每次只对一个叶子节点的数据进行更新,所以相当于更改了树上的一条链。
2.
我们不在原来的树上修改,而是创建若干个新的节点组成一条链代表树中修改后的各个节点 (下图红色) ,然后直接把这条链糊到树上, 并且让新链中的每个节点都连好它在树中应该连的节点 (下图蓝色) 。
tipB:
你会发现只要交换新旧链, 就可以得到两颗完整的树
tipC:
你还会发现:在每次更新中添加的新链,都会包含树的根节点,所以你只需要记录下第[0 - N]次操作后的树的根节点,就可以通过某一个根节点得到特定历史版本的树。
(0次操作后的根节点是黑色树根, 1次操作后的根节点是红色树根)
3.
然 后 随 便 van van , 主 席 树 就 学 完 了。
4.
然后我们就可以滚到别的题解上爬了
主席树 luogu.com.cn 的主席树板子
纯区间第K大
区间[r - (l - 1)] 相当于只插入了[l - r] 然后直接找。
#include <bits/stdc++.h>
const int N = 2e5+4;
int lst[N]/*原序列*/, srt[N]/*排序数组*/, root[N]/*记录树根*/;
int sum[N<<5], L[N<<5], R[N<<5];
int n, m, ql, qr, k, idx, sn;
int build(int l, int r){
int rt = ++idx, mid = (l + r) >> 1;
if(l < r){
L[rt] = build(l, mid);
R[rt] = build(mid + 1, r);
}
return rt;
}
int update(int pre, int l, int r, int k){
int rt = ++idx, mid = (l + r) >> 1;
L[rt] = L[pre], R[rt] = R[pre], sum[rt] = sum[pre] + 1;
if(l < r){
if(k <= mid) L[rt] = update(L[pre], l, mid, k);
else R[rt] = update(R[pre], mid + 1, r, k);
}
return rt;
}
int query(int ql, int qr, int l, int r, int k){
if( l >= r) return l;
int num = sum[L[qr]] - sum[L[ql]], mid = (l + r) >> 1;
if(num >= k) return query(L[ql], L[qr], l, mid, k);
else return query(R[ql], R[qr], mid + 1, r, k - num);
}
int main(){
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++){
scanf("%d", &lst[i]);
srt[i] = lst[i];
}
std::sort(srt + 1, srt + 1 + n);
sn = std::unique(srt + 1, srt + 1 + n) - srt - 1;
root[0] = build(1, sn);
for(int i=1; i<=n; i++){
k = std::lower_bound(srt + 1, srt + 1 + sn, lst[i]) - srt;
root[i] = update(root[i - 1], 1, sn, k);
}
while(m --){
scanf("%d%d%d", &ql, &qr, &k);
n = query(root[ql - 1], root[qr], 1, sn, k);
printf("%d\n", srt[n]);
}
return 0;
}
主席树+并查集 luogu.com.cn 的可持久化并查集板子
纯并查集
#include <bits/stdc++.h>
const int N = 1e5 + 4, M = 2e5 + 4;
int n, q, idx, x, y, op, xx, yy;
int D[M << 5], F[M << 5], L[M << 5], R[M << 5], E[M];
int build(int l, int r){
int rt = ++idx;
if(l == r) { F[rt] = l, D[rt] = 1; return rt; }
int mid = (l + r) >> 1;
L[rt] = build(l, mid);
R[rt] = build(mid + 1, r);
return rt;
}
int update(int pre, int l, int r, int k, int f){
int rt = ++idx;
if(l == r){
D[rt] = D[pre];
F[rt] = f;
return rt;
}
L[rt] = L[pre], R[rt] = R[pre];
int mid = (l + r) >> 1;
if(k <= mid) L[rt] = update(L[pre], l, mid, k , f);
else R[rt] = update(R[pre], mid + 1, r, k, f);
return rt;
}
int query(int rt, int l, int r, int k){
if(l == r) return rt;
int mid = (l + r) >> 1;
if(k <= mid) return query(L[rt], l, mid, k);
return query(R[rt], mid + 1, r, k);
}
void cd(int rt, int l, int r, int k){
if(l == r) {D[rt] += 1; return ;}
int mid = (l + r) >> 1;
if(k <= mid) cd(L[rt], l, mid, k);
else cd(R[rt], mid + 1, r, k);
}
int ffind(int rt, int x){
int t = query(rt, 1, n, x);
if(x == F[t]) return t;
return ffind(rt, F[t]);
}
int main(){
scanf("%d%d", &n, &q);
E[0] = build(1, n);
for(int i=1; i<=q; i++){
scanf("%d", &op);
if(op == 1){
scanf("%d%d", &x, &y);
E[i] = E[i - 1];
xx = ffind(E[i], x), yy = ffind(E[i], y);
if(D[xx] > D[yy]) std::swap(xx, yy);
E[i] = update(E[i - 1], 1, n, F[xx], F[yy]);
if(D[xx] + 1 > D[yy]) cd(E[i], 1, n, F[yy]);
}
if(op == 2){
scanf("%d", &x);
E[i] = E[x];
}
if(op == 3){
scanf("%d%d", &x, &y);
E[i] = E[i - 1];
xx = ffind(E[i], x), yy = ffind(E[i], y);
printf("%d\n", F[xx] == F[yy] ? 1 : 0);
}
}
return 0;
}