樹上主席樹+lca Count on a tree(洛谷 P2633)

Count on a tree

題目描述

給定一棵 n 個節點的樹,每個點有一個權值。有 m 個詢問,每次給你 u,v,k,你需要回答 u xor last 和 v 這兩個節點間第 k 小的點權。

其中 last 是上一個詢問的答案,定義其初始爲 0,即第一個詢問的 u 是明文。

輸入格式

第一行兩個整數 n,m。

第二行有 n 個整數,其中第 i 個整數表示點 i 的權值。

後面 n−1 行每行兩個整數 x,y,表示點 x 到點 y 有一條邊。

最後 m 行每行兩個整數 u,v,k 表示一組詢問。

輸出格式

m 行,每行一個正整數表示每個詢問的答案。


主席樹十分明顯,但是這是樹上問題,不是線性區間;

其實是一樣的,線性區間的主席樹就是前綴和概念,樹也可以這樣運用,就是樹上前綴和;

每個點是它的父親連接過來的,所以必須遞歸建主席樹;

那麼u,v區間的第k大不就是tr[u]+tr[v]-tr[lca(u,v)]-tr[fa(lca(u,v))] ;就是前綴和概念;

代碼:

#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=100100;
const int M=50100;
const LL mod=1e9+7;
inline int read(){//非常重要的快速讀入代碼
	int x=0,sign=1;
	char c=getchar();
	while(c>'9'||c<'0'){//判斷符號
		if(c=='-') sign=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){//轉換數
		x=x*10+c-'0';
		c=getchar();
	}
	return x*sign;
}
int n,m,ss,head[N],cnt,a[N],b[N],ans[N];
int size[N],fa[N],son[N],deep[N];
int top[N];
struct Node{
	int to,nex;
}edge[N*2];
void add(int p,int q){edge[cnt].to=q,edge[cnt].nex=head[p],head[p]=cnt++;}
void dfs1(int p,int fat){
	size[p]=1,fa[p]=fat;
	int mx=-1;
	for(int i=head[p];~i;i=edge[i].nex){
		int q=edge[i].to;
		if(q!=fat){
			deep[q]=deep[p]+1;
			dfs1(q,p);
			size[p]+=size[q];
			if(size[q]>mx) son[p]=q,mx=size[q];
		}
	}
}
void dfs2(int p,int topp){
	top[p]=topp;
	if(!son[p]) return;//沒有重兒子,也就是葉子結點 
	dfs2(son[p],topp);//先處理重兒子 
	for(int i=head[p];~i;i=edge[i].nex){
		int q=edge[i].to;
		if(q==fa[p]||q==son[p]) continue;
		dfs2(q,q);
	}
}
int lca(int x,int y){
	while(top[x]!=top[y]){
		if(deep[top[x]]<deep[top[y]]) swap(x,y);//保證x結點的頂點更深 
		x=fa[top[x]]; 
	}
	if(deep[x]<deep[y]) swap(x,y);//保證x結點的頂點更深 
	return y;
}
////
int tr[N],L[N*40],R[N*40],sum[N*40],root;
int build(int l,int r){
	int rt=++root;
	int d=(l+r)>>1;
	if(l<r){
		L[rt]=build(l,d);
		R[rt]=build(d+1,r);
	}
	return rt;
}
int update(int pre,int l,int r,int pos){
	int rt=++root;
	L[rt]=L[pre],R[rt]=R[pre],sum[rt]=sum[pre]+1;
	int d=(l+r)>>1;
	if(l<r){
		if(pos<=d) L[rt]=update(L[pre],l,d,pos);
		else R[rt]=update(R[pre],d+1,r,pos);
	}
	return rt;
}
int query(int r1,int r2,int r3,int r4,int l,int r,int k){
	if(l==r) return l;
	int s=sum[L[r1]]+sum[L[r2]]-sum[L[r3]]-sum[L[r4]];
	int d=(l+r)>>1;
	if(s>=k) return query(L[r1],L[r2],L[r3],L[r4],l,d,k);
	else return query(R[r1],R[r2],R[r3],R[r4],d+1,r,k-s);
}
void dfs(int p,int ft){
	tr[p]=update(tr[ft],1,ss,a[p]);
	for(int i=head[p];~i;i=edge[i].nex){
		int q=edge[i].to;
		if(q!=ft){
			dfs(q,p);
		}
	}
}
////
int main(){
	memset(head,-1,sizeof(head));
	n=read(),m=read(); 
	for(int i=1;i<=n;i++) a[i]=read(),b[i]=a[i];
	for(int i=1;i<n;i++){
		int u,v;u=read(),v=read();
		add(u,v),add(v,u);
	}
	///離散化 
	sort(b+1,b+n+1);
	ss=unique(b+1,b+n+1)-b-1;
	for(int i=1;i<=n;i++){
		int t=lower_bound(b+1,b+ss+1,a[i])-b;
		ans[t]=a[i];
		a[i]=t;
	}
	///
	dfs1(1,0),dfs2(1,1);
	tr[0]=build(1,ss);
	dfs(1,0);//遞歸的建主席樹 
//	for(int i=1;i<=n;i++) tr[i]=update(tr[fa[i]],1,ss,a[i]);//建主席樹 
	int last=0;
	while(m--){
		int u,v,k;u=read(),v=read(),k=read();
		u^=last;
		last=ans[query(tr[u],tr[v],tr[lca(u,v)],tr[fa[lca(u,v)]],1,ss,k)];
		printf("%d\n",last);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章