主席树
作者:互联网
主席树:可持久化线段树,可以支持查询历史版本的线段树。
在模板题中,对数组的每一个前缀建立一棵权值线段树,线段树的每一个节点维护属于区间\([l,r]\)的数的个数。
对于询问\([l_i,r_i]\)中的第\(k_i\)大的数,利用前缀的思想,版本\(r_i\)与\(l_{i-1}\)之间的差就是\([l_i,r_i]\)的信息。左儿子中数的个数为\(sum_l\),如果\(sum_l \ge k_i\),继续查询左儿子中第\(k_i\)大的数,如果\(sum_l<k_i\),查询右儿子中第\(k_i-sum_l\)大的数。
如果对于每个版本都建立一棵线段树,会MLE。但两个相邻版本的线段树,对一个节点,只会改变一个儿子,另一个儿子没有改变。没有改变的儿子用上一个版本的儿子,发生的儿子新建一个节点,所以要动态开点。
权值线段树维护的是\([l,r]\)中数的个数,所以要排序之后离散化,重要的是大小,不是数值。查询的结果是第\(k\)大的数在原数组是第几大的
查找和更新的时间复杂度为\(O(logn)\).
空树的空间复杂度为\(O(nlogn)\),更新的空间复杂度为\(O(logn)\) ,所以内存池至少开\(32n\)
代码:
点击查看代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 2E5+5;
typedef long long ll;
inline int read(){//读入优化
char ch; int flag = 1;
while((ch=getchar())<'0'||ch>'9')
if(ch == '-') flag = -1;
int res = ch-48;
while((ch=getchar())>='0'&&ch<='9')
res = res*10+ch-48;
return res*flag;
}
int n, q, m, cnt = 0;
int a[maxn], b[maxn], root[maxn];
int sum[maxn<<5], ls[maxn<<5], rs[maxn<<5];
inline int build(int l, int r){
int rt = ++cnt;
sum[rt] = 0;
if(l == r)
return rt;
int mid = (l+r) >> 1;
ls[rt] = build(l, mid);
rs[rt] = build(mid+1, r);
return rt;
}
inline int update(int pre, int l, int r, int x){
int rt = ++cnt;
ls[rt] = ls[pre], rs[rt] = rs[pre], sum[rt] = sum[pre]+1;
if(l == r)
return rt;
int mid = (l+r) >> 1;
if(x <= mid)
ls[rt] = update(ls[pre], l, mid, x);
else
rs[rt] = update(rs[pre], mid+1, r, x);
return rt;
}
inline int query(int x, int y, int l, int r, int k){
if(l == r)
return l;
int mid = (l+r) >> 1;
int tmp = sum[ls[y]] - sum[ls[x]];
if(tmp >= k)
return query(ls[x], ls[y], l, mid, k);
else
return query(rs[x], rs[y], mid+1, r, k-tmp);
}
int main(){
n = read(), q = read();
for(int i = 1; i <= n; i++)
a[i] = b[i] = read();
sort(b+1, b+1+n);
m = unique(b+1, b+1+n)-(b+1);
for(int i = 1; i <= n; i++){
int s = lower_bound(b+1, b+1+m, a[i])-b;
root[i] = update(root[i-1], 1, m, s);
}
int x, y, k;
for(int i = 1; i <= q; i++){
x = read(), y = read(), k = read();
int t = query(root[x-1], root[y], 1, m, k);
cout << b[t] << endl;
}
return 0;
}
主席树维护的是前缀区间信息。设根节点是root,版本rt[i]维护的是root~i路径上的题。则u到v路径上的信息在\(rt[u]+rt[v]-rt[lca]-rt[fa[lca]]\)中,然后在树上查询第\(k\)小.
在树剖dfs的时候顺便建立线段树,数据离散化
代码:
点击查看代码
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e5+10;
int fa[maxn], siz[maxn], top[maxn], son[maxn], dep[maxn];
struct edge{
int to, nxt;
}e[maxn<<1];
int head[maxn], tot;
int sum[maxn<<5], ls[maxn<<5], rs[maxn<<5], root[maxn];
int a[maxn], b[maxn];
int n, m, q, cnt, ans;
void add(int u, int v){
e[++tot].nxt = head[u];
e[tot].to = v;
head[u] = tot;
}
int update(int pre, int l, int r, int x){
int rt = ++cnt;
sum[rt] = sum[pre] + 1;
if(l == r)
return rt;
int mid = (l+r) >> 1;
if(x <= mid){
ls[rt] = update(ls[pre], l, mid, x);
rs[rt] = rs[pre];
}
else{
rs[rt] = update(rs[pre], mid+1, r, x);
ls[rt] = ls[pre];
}
return rt;
}
void dfs1(int u){
siz[u] = 1;
dep[u] = dep[fa[u]] + 1;
root[u] = update(root[fa[u]], 1, m, a[u]);
for(int i = head[u]; i; i = e[i].nxt){
int v = e[i].to;
if(v == fa[u])
continue;
fa[v] = u;
dfs1(v);
siz[u] += siz[v];
if(!son[u] || siz[v] > siz[son[u]])
son[u] = v;
}
}
void dfs2(int u, int tp){
top[u] = tp;
if(!son[u])
return;
dfs2(son[u], tp);
for(int i = head[u]; i; i = e[i].nxt){
int v = e[i].to;
if(v == fa[u] || v == son[u])
continue;
dfs2(v, v);
}
}
int LCA(int x, int y){
while(top[x] != top[y]){
if(dep[top[x]] >= dep[top[y]])
x = fa[top[x]];
else
y = fa[top[y]];
}
return dep[x] >= dep[y] ? y : x;
}
int query(int ql, int qr, int lca, int lca_fa, int l ,int r, int k){
if(l == r)
return l;
int x = sum[ls[ql]] + sum[ls[qr]] - sum[ls[lca]] - sum[ls[lca_fa]];
int mid = (l+r) >> 1;
if(x >= k)
return query(ls[ql], ls[qr], ls[lca], ls[lca_fa], l, mid, k);
else
return query(rs[ql], rs[qr], rs[lca], rs[lca_fa], mid+1, r, k-x);
}
inline ll read(){//读入优化
char ch;
ll sign = 1;
while((ch=getchar())<'0'||ch>'9')
if(ch == '-')
sign = -1;
ll res = ch-48;
while((ch=getchar())>='0'&&ch<='9')
res = res*10+ch-48;
return res*sign;
}
int main(){
n = read(), q = read();
for(int i = 1; i <= n; i++){
a[i] = b[i] = read();
}
sort(b+1, b+1+n);
m = unique(b+1, b+1+n) - (b+1);
for(int i = 1; i <= n; i++)
a[i] = lower_bound(b+1, b+1+m, a[i]) - b;
for(int i = 1; i < n; i++){
int u, v;
u = read(), v = read();
add(u, v);
add(v, u);
}
dfs1(1);
dfs2(1, 1);
while(q--){
int x, y, k, lca;
x = read(), y = read(), k = read();
x ^= ans;
lca = LCA(x, y);
ans=b[query(root[x],root[y],root[lca],root[fa[lca]],1,m,k)];
cout << ans << endl;
}
return 0;
}
标签:rt,ch,int,sum,ls,lca,主席 来源: https://www.cnblogs.com/Ruthenium/p/16142779.html