Count on a tree

该博客介绍了如何使用树上主席树和最近公共祖先(LCA)算法来解决区间查询问题。文章通过luoguP2633题目详细解释了数据结构的构建、更新及查询过程,包括建立零号主席树、ST表的构建以及查询函数的实现。此外,还展示了如何处理输入并输出查询结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

树上主席树练习

luogu P2633

#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 100000
#define NlogN 2500000 
using namespace std;
int n,m,a[N+1],b[N+1],tree[N+1],l[NlogN],r[NlogN],sum[NlogN],deep[N+1],f[N+1][31],tot,len,mid,u,v,k,last;
int cnt,he[N<<2],nxt[N<<2],to[N<<2];
void add(int u,int v)
{
	cnt++;
	nxt[cnt]=he[u];
	he[u]=cnt;
	to[cnt]=v;
}
int build(int ll,int rr)
{
	int rt=++tot;
	if(ll<rr)
	{
		mid=(ll+rr)>>1;
		l[rt]=build(ll,mid);
		r[rt]=build(mid+1,rr);
	}
	return rt;
}
int update(int pre,int ll,int rr,int x)
{
	int rt=++tot;
	l[rt]=l[pre],r[rt]=r[pre],sum[rt]=sum[pre]+1;
	if(ll<rr)
	{
	  mid=(ll+rr)>>1;
	  if(x<=mid) l[rt]=update(l[pre],ll,mid,x);
	  else r[rt]=update(r[pre],mid+1,rr,x);	
	}
	return rt;
}
void build_tree(int pre,int now)
{
	deep[now]=deep[pre]+1,f[now][0]=pre;
	int rank=lower_bound(b+1,b+len+1,a[now])-b;
	tree[now]=update(tree[pre],1,len,rank);
	for(int i=he[now];i;i=nxt[i])
	{
		int g=to[i];
		if(g==pre) continue;
		build_tree(now,g);
	}
}
int lca(int q,int p)
{
	if(deep[q]>deep[p]) swap(q,p);
	int cz=deep[p]-deep[q],js=0;
	while(cz)
	{
		if(cz&1) p=f[p][js];
		js++,cz>>=1;
	}
	if(q==p) return q;
	for(int i=20;i>=0;i--) if(f[q][i]!=f[p][i]) q=f[q][i],p=f[p][i];
	return f[q][0];
}
int query(int t1,int t2,int t3,int t4,int ll,int rr,int id)
{
	if(ll==rr) return ll;
	int cz=sum[l[t1]]+sum[l[t2]]-sum[l[t3]]-sum[l[t4]];
	mid=(ll+rr)>>1;
	if(cz>=id) return query(l[t1],l[t2],l[t3],l[t4],ll,mid,id);
	else return query(r[t1],r[t2],r[t3],r[t4],mid+1,rr,id-cz);
}
int qread()
{
	char c=getchar();
	int x=0;
	while(c>'9'||c<'0') c=getchar();
	while(c<='9'&&c>='0') x=(x<<3)+(x<<1)+c-'0',c=getchar();
	return x;
}
void ks(int xx)
{
	if(xx>=10) ks(xx/10);
	putchar(xx%10+'0');
}
int main()
{
	n=qread(),m=qread();
	for(int i=1;i<=n;i++) a[i]=qread(),b[i]=a[i];
	sort(b+1,b+n+1);
	len=unique(b+1,b+n+1)-b-1;
	tree[0]=build(1,len);//建零号主席树 
	for(int i=1,x,y;i<=n-1;i++) x=qread(),y=qread(),add(x,y),add(y,x);
	build_tree(0,1); 
	for(int i=1;i<=20;i++) //ST表 
	for(int j=1;j<=n;j++) f[j][i]=f[f[j][i-1]][i-1];
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&u,&v,&k);
		last=u^last;
		int LCA=lca(last,v);
		last=b[query(tree[last],tree[v],tree[LCA],tree[f[LCA][0]],1,len,k)];
		ks(last),putchar('\n');
	}
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值