主席树复习笔记
主席树复习笔记
首先简单复习一下之前学过的主席树和线段树合并的题目
因为主席树打的比较熟,所以就稍微简单一些吧。
LuoguP3834 【模板】可持久化线段树 1(主席树)
非常形象的一个图
本质是一颗权值线段树?
我们每次加入一个点,发现最多只有\(log\)个点会受到影响,所以我们就把这\(log\)个点单独建出来,其余的直接套用之前的就好了
这大概就是主席树的核心思想了?
#include<cstdio>
#include<cctype>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 2e5 + 3;
struct node{
int sum;
int lc,rc;
}a[N * 20];
int t,n,m;
int A[N],B[N];
int rt[N];
inline int read(){
int v = 0,c = 1;char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') c = -1;
ch = getchar();
}
while(isdigit(ch)){
v = v * 10 + ch - 48;
ch = getchar();
}
return v * c;
}
inline void ins(int &u,int l,int r,int x){
/// printf("%d %d %d %d\n",u,l,r,x);
a[++t] = a[u];
u = t;
if(l == r){
a[u].sum++;
return ;
}
int mid = (l + r) >> 1;
if(x <= mid) ins(a[u].lc,l,mid,x);
else ins(a[u].rc,mid + 1,r,x);
a[u].sum = a[a[u].lc].sum + a[a[u].rc].sum;
}
inline int query(int u1,int u2,int l,int r,int p){
//printf("%d %d")
if(l == r) return l;
int w = a[a[u2].lc].sum - a[a[u1].lc].sum;
int mid = (l + r) >> 1;
if(w >= p) return query(a[u1].lc,a[u2].lc,l,mid,p);
else return query(a[u1].rc,a[u2].rc,mid + 1,r,p - w);
}
int main(){
n = read(),m = read();
for(int i = 1;i <= n;++i) B[i] = A[i] = read();
sort(B + 1,B + n + 1);
B[0] = unique(B + 1,B + n + 1) - B - 1;
// for(int i = 1;i <= B[0];++i) printf("%d ",B[i]);puts("");
for(int i = 1;i <= n;++i){
rt[i] = rt[i - 1];
A[i] = lower_bound(B + 1,B + B[0] + 1,A[i]) - B;
// cout << A[i] << endl;
ins(rt[i],1,B[0],A[i]);
}
while(m--){
int l = read(),r = read(),p = read();
printf("%d\n",B[query(rt[l - 1],rt[r],1,B[0],p)]);
}
return 0;
}
线段树合并
线段树合并可以看做是树上启发式合并的一种优化。时间复杂度我不会证明
但应该不会比启发式合并慢
我们在树上的每个点都维护一颗线段树,在dfs中将其与儿子的信息合并为一颗
合并完成后,该线段树维护的就是子树信息了。
这也是线段树合并大多数都是离线的原因。因为线段树继续向上合并时,维护的内容会改变。
这就要求在合并完成时就求出相应答案
例题
LuoguP3605
简单的线段树合并裸题(当然启发式合并也可以过去)
大概思路就和上面所说的
将儿子的线段树全部合并之后
查一下有多少比他权值大的,跟新答案就好了
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<iostream>
#include<vector>
using namespace std;
const int N = 1e5 + 3;
int n,m,t;
int v[N];
int b[N];
int fa[N];
int rt[N],ans[N];
vector <int> G[N];
struct node{
int sum;
int lc,rc;
}a[N << 5];
inline int read(){
int v = 0,c = 1;char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') c = -1;
ch = getchar();
}
while(isdigit(ch)){
v = v * 10 + ch - 48;
ch = getchar();
}
return v * c;
}
inline void pushup(int u){a[u].sum = a[a[u].lc].sum + a[a[u].rc].sum;}
inline void ins(int &u,int l,int r,int x){
if(!u) u = ++t;
// printf("%d %d %d %d %d\n",u,l,r,x,a[u].sum);
if(l == r) {
a[u].sum++;
return ;
}
int mid = (l + r) >> 1;
if(x <= mid) ins(a[u].lc,l,mid,x);
else ins(a[u].rc,mid + 1,r,x);
pushup(u);
}
inline void merge(int &u1,int u2,int l,int r){
if(!u1) {u1 = u2;return;}
if(!u2) return;
if(l == r){
a[u1].sum += a[u2].sum;
return ;
}
int mid = (l + r) >> 1;
merge(a[u1].lc,a[u2].lc,l,mid);
merge(a[u1].rc,a[u2].rc,mid + 1,r);
pushup(u1);
}
inline int query(int u,int l,int r,int x){
if(!u) return 0;
// printf("%d %d %d %d %d\n",u,l,r,a[u].sum,x);
if(l > x) return a[u].sum;
if(r <= x) return 0;
int mid = (l + r) >> 1;
if(mid <= x) return query(a[u].rc,mid + 1,r,x);
else return query(a[u].lc,l,mid,x) + query(a[u].rc,mid + 1,r,x);
}
inline void dfs(int x,int f){
fa[x] = f;
for(int i = 0;i < (int)G[x].size();++i){
int y = G[x][i];
if(y == f) continue;
dfs(y,x);
merge(rt[x],rt[y],1,b[0]);
}
// printf("xxxxxx:%d\n",x);
ans[x] = query(rt[x],1,b[0],v[x]);
ins(rt[x],1,b[0],v[x]);
}
int main(){
n = read();
for(int i = 1;i <= n;++i) b[i] = v[i] = read();
sort(b + 1,b + n + 1);
b[0] = unique(b + 1,b + n + 1) - b - 1;
for(int i = 1;i <= n;++i)
v[i] = lower_bound(b + 1,b + b[0] + 1,v[i]) - b;
//for(int i = 1;i <= n;++i) printf("))%d\n",v[i]);
for(int i = 2;i <= n;++i){
int x = read();
G[i].push_back(x);
G[x].push_back(i);
}
dfs(1,0);
for(int i = 1;i <= n;++i) printf("%d\n",ans[i]);
return 0;
}
luoguP4556 [Vani有约会]雨天的尾
还是一道较简单的题目,思路和上一道题目一样,转化成树上差分,然后在线段树上动动手脚
#include<cstdio>
#include<cstring>
#include<cctype>
#include<vector>
#include<iostream>
#include<algorithm>
#define mk make_pair
using namespace std;
const int N = 1e5 + 3;
vector < pair<int,int> > G[N];
struct node{
int to;
int nxt;
}e[N << 1];
int head[N],fa[N][20],deep[N];
int rt[N];
int ans[N];
struct tree{
int sum;
int pos;
int lc,rc;
}a[N * 25];
int n,m,t,tot,maxx;
inline void add(int x,int y){
e[++tot].to = y;
e[tot].nxt = head[x];
head[x] = tot;
}
inline int read(){
int v = 0,c = 1;char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') c = -1;
ch = getchar();
}
while(isdigit(ch)){
v = v * 10 + ch - 48;
ch = getchar();
}
return v * c;
}
inline void dfs1(int x,int f,int dep){
deep[x] = dep;
fa[x][0] = f;
for(int i = head[x];i;i = e[i].nxt){
int y = e[i].to;
if(y == f) continue;
dfs1(y,x,dep + 1);
}
}
inline int lca(int x,int y){
if(deep[x] < deep[y]) swap(x,y);
for(int i = 19;i >= 0;--i)
if(deep[fa[x][i]] >= deep[y]) x = fa[x][i];
if(x == y) return x;
for(int i = 19;i >= 0;--i)
if(fa[x][i] != fa[y][i]) x = fa[x][i],y = fa[y][i];
return fa[x][0];
}
inline void pushup(int u){
if(a[a[u].lc].sum >= a[a[u].rc].sum) a[u].sum = a[a[u].lc].sum,a[u].pos = a[a[u].lc].pos;
else a[u].sum = a[a[u].rc].sum,a[u].pos = a[a[u].rc].pos;
}
inline void ins(int &u,int l,int r,int x,int v){
if(!u) u = ++t;
if(l == r){
a[u].sum += v;
if(a[u].sum > 0) a[u].pos = x;
else a[u].pos = 0;
// printf("%d %d %d %d %d\n",u,l,r,a[u].sum,a[u].pos);
return ;
}
int mid = (l + r) >> 1;
if(x <= mid) ins(a[u].lc,l,mid,x,v);
else ins(a[u].rc,mid + 1,r,x,v);
pushup(u);
//printf("%d %d %d %d %d\n",u,l,r,a[u].sum,a[u].pos);
}
inline void merge(int &u1,int u2,int l,int r){
//printf("%d %d %d %d %d %d\n",u1,u2,l,r,a[u1].sum,a[u2].sum);
if(!u1) {u1 = u2;return;}
if(!u2) return ;
if(l == r){
//printf("%d %d %d %d\n",u1,u2,a[u1].sum,a[u2].sum);
a[u1].sum += a[u2].sum;
if(a[u1].sum > 0) a[u1].pos = l;
else a[u1].pos = 0;
return ;
}
int mid = (l + r) >> 1;
merge(a[u1].lc,a[u2].lc,l,mid);
merge(a[u1].rc,a[u2].rc,mid + 1,r);
pushup(u1);
}
inline void dfs2(int x,int f){
for(int i = head[x];i;i = e[i].nxt){
int y = e[i].to;
if(y == f) continue;
dfs2(y,x);
// printf("x:%d y:%d\n",x,y);
merge(rt[x],rt[y],1,maxx);
}
// printf("now:%d\n",x);
for(int i = 0;i < (int)G[x].size();++i){
int z = G[x][i].first,s = G[x][i].second;
// printf("%d %d\n",z,s);
ins(rt[x],1,maxx,z,s);
}
ans[x] = a[rt[x]].pos;
}
int main(){
// freopen("data.in","r",stdin);
//freopen("data2.out","w",stdout);
n = read(),m = read();
for(int i = 1;i < n;++i){
int x = read(),y = read();
add(x,y);
add(y,x);
}
dfs1(1,0,1);
// for(int i = 1;i <= n;++i) printf("%d\n",deep[i]);
for(int j = 1;j < 20;++j)
for(int i = 1;i <= n;++i)
fa[i][j] = fa[fa[i][j - 1]][j - 1];
while(m--){
int x = read(),y = read(),z = read();
maxx = max(maxx,z);
G[x].push_back(mk(z,1));
G[y].push_back(mk(z,1));
int LCA = lca(x,y);
G[LCA].push_back(mk(z,-1));
// printf("%d %d\n",LCA,fa[LCA][0]);
G[fa[LCA][0]].push_back(mk(z,-1));
}
dfs2(1,0);
//printf("%d %d\n",a[rt[3]].sum,a[rt[3]].pos);
for(int i = 1;i <= n;++i) printf("%d\n",ans[i]);
return 0;
}
写的挺简略的,数据结构的题目主要还是要以刷题为主吧