主席树复习笔记

主席树复习笔记

首先简单复习一下之前学过的主席树和线段树合并的题目
因为主席树打的比较熟,所以就稍微简单一些吧。

LuoguP3834 【模板】可持久化线段树 1(主席树)

AydRte.png

非常形象的一个图
本质是一颗权值线段树?
我们每次加入一个点,发现最多只有\(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;	
}

写的挺简略的,数据结构的题目主要还是要以刷题为主吧

posted @ 2019-04-01 21:40  wyxdrqcccc  阅读(136)  评论(0编辑  收藏  举报