P2633 Count on a tree 主席树求树上第k大

看到树上第k大 首先想想普通的前缀和怎么做  假如我要静态的求链(u,v)的和  那么我们就从树根一直搜下来 维护一个前缀和,答案就是 sum[u]+sum[v]-sum[lca(u,v)]-sum[fa[lc[u,v]]]

现在再来看主席树的方法就一目了然啦 对于每一个结点 在父节点的基础上建树 然后询问的时候

观察 sum[lc[u]]+sum[lc[v]]-sum[lc[lca(u,v)]]-sum[lc[fa[lca(u,v)]]] 和k 的大小关系即可

#include<bits/stdc++.h>
#define I inline void
#define R register int
using namespace std;
const int N = 1e5+100;
int n,m;
int sum[N*40],lc[N*40],rc[N*40],rt[N],tot;
int fa[N][30],lg[N],dep[N];
int cur,h[N],nex[N<<1],to[N<<1];
int has[N],val[N],cnt;
inline int in(){
	int w=0,x=0;char c=0;
	while(c>'9'||c<'0') w|=c=='-',c=getchar();
	while(c<='9'&&c>='0') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return w?-x:x;
}
inline int gethas(int x){
	return lower_bound(has+1,has+1+cnt,x)-has;
}
I add_edge(R x,R y){
	to[++cur]=y;nex[cur]=h[x];h[x]=cur;
}
inline int lca(R x,R y){
	if(dep[x]<dep[y]) swap(x,y);
	while(dep[x]>dep[y]) x=fa[x][lg[dep[x]-dep[y]]-1];
	if(x==y) return x;
	for(int i = lg[dep[x]-1]; i >= 0; i--)
		if(fa[x][i]!=fa[y][i]) 
		x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}
void update(int &rt,R las,R l,R r,R pos){
	rt=++tot;
	sum[rt]=sum[las]+1;lc[rt]=lc[las];rc[rt]=rc[las];
	if(l==r) return;
	int mid = l+r>>1;
	if(pos<=mid) update(lc[rt],lc[las],l,mid,pos);
	else update(rc[rt],rc[las],mid+1,r,pos);
} 
int query(R u,R v,R lca,R flc,R l,R r,R k){
	if(l==r) return l;
	R g = sum[lc[u]]+sum[lc[v]]-sum[lc[lca]]-sum[lc[flc]];
	R mid = l+r>>1;
	if(k<=g) return query(lc[u],lc[v],lc[lca],lc[flc],l,mid,k);
	else return query(rc[u],rc[v],rc[lca],rc[flc],mid+1,r,k-g); 
}
void dfs(int u,int fath){
	fa[u][0]=fath,dep[u]=dep[fath]+1;
	for(int i = 1; i <= lg[dep[u]]; i++) 
	fa[u][i]=fa[fa[u][i-1]][i-1];
	update(rt[u],rt[fath],1,cnt,gethas(val[u]));
	for(int i = h[u]; i; i = nex[i]){
		int v = to[i];
		if(v==fath) continue;
		dfs(v,u);
	}
}
int main(){
	n=in(),m=in();
	for(R i = 1; i < N; i++) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
	for(R i = 1; i <= n; i++) 
	val[i]=in(),has[i]=val[i];
	sort(has+1,has+1+n);
	cnt=unique(has+1,has+1+n)-has;
	for(R i = 1; i <= n-1; i++){
		R u,v;
		u=in(),v=in();
		add_edge(u,v);add_edge(v,u);
	}
	dfs(1,0);
	int last=0;
	//printf("tot=%d cur=%d cnt=%d\n",tot,cur,cnt);
	for(R i = 1; i <= m; i++){
		R u,v,k;
		u=in(),v=in(),k=in();
		u^=last;
		int lcauv=lca(u,v);
		//printf("u=%d v=%d lcauv=%d\n",u,v,lcauv);
		last=has[query(rt[u],rt[v],rt[lcauv],rt[fa[lcauv][0]],1,cnt,k)];
		printf("%d\n",last);
	}
	return 0;
} 

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值