长链剖分 - lxhgww的奇思妙想(Vijos)

传送门


Analysis

长链剖分(可能是重链剖分他兄弟吧)
我们预处理出每个点向上2的幂次祖先fa[k][u],每一条重链的链顶向上、向下重链长度个点。(储存在vector中,由于重链长度和不超过n,所以空间、时间都是O(n)的)
对于询问求u的k次祖先,我们可以拆成r+(k−r)次祖先,其中r是k的最高二进制位,r次祖先u·可以用fa[k][u]得到。由于性质1。所以我们可以在top[u`]处的vector中查询。


Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<vector>
#define re register
#define in read()
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){
		res=(res<<1)+(res<<3)+(ch^48);
		ch=getchar();
	}
	return f==1?res:-res;
}
const int N=3e5+10;
int n,m;
int nxt[N<<1],to[N<<1],head[N],ecnt=0;
inline void add(int x,int y){
	nxt[++ecnt]=head[x];head[x]=ecnt;to[ecnt]=y;
}
int dep[N],fa[N][25],md[N],son[N];
int len[N],top[N];
void dfs1(int u,int fu){
	md[u]=dep[u]=dep[fu]+1;fa[u][0]=fu;
	for(re int i=1;i<20;++i)
	    if(fa[u][i-1]) fa[u][i]=fa[fa[u][i-1]][i-1];
		else break;
	for(re int e=head[u];e;e=nxt[e]){
		int v=to[e];
		if(v==fu) continue;
		dfs1(v,u);
		if(md[son[u]]<md[v]) son[u]=v,md[u]=md[v];
	}
}
void dfs2(int u){
	len[u]=md[u]-dep[top[u]]+1;
	if(son[u]){
		top[son[u]]=top[u];
		dfs2(son[u]);
	}
	for(re int e=head[u];e;e=nxt[e]){
		int v=to[e];
		if(v==fa[u][0]||v==son[u]) continue;
		top[v]=v;
		dfs2(v);
	}
}
vector<int> U[N],D[N];
int bit[N];//每个值的二进制位上最高位1的位置 
inline int query(int x,int k){
	if(k>dep[x]) return 0;if(!k) return x;
	int d1=bit[k];k^=(1<<bit[k]);
	int u=fa[x][d1];
	if(!k) return u;
	if(dep[u]-dep[top[u]]==k) return top[u];
	if(dep[u]-dep[top[u]]>k) return D[top[u]][dep[u]-dep[top[u]]-k-1];
	return U[top[u]][k-(dep[u]-dep[top[u]])-1];
}
int main(){
	n=in;
	for(re int i=1;i<n;++i){
		int x=in,y=in;
		add(x,y);add(y,x);
	}
	dfs1(1,0);
	top[1]=1;dfs2(1);
	for(re int i=1;i<=n;++i)
		if(i==top[i]){//处理每条重链的链端,向上、向下链长长度的节点有哪些 
			int l=0,x=i;
			while(l<len[i]&&x) x=fa[x][0],l++,U[i].push_back(x);///
			l=0,x=i;
			while(l<len[i]) x=son[x],l++,D[i].push_back(x);///
		}
	int cnt=1;
	for(re int i=1;i<=n;++i){
		if((i>>cnt)&1) cnt++;
		bit[i]=cnt-1;
	}
	int ans=0;
	m=in;
	for(re int i=1;i<=m;++i){
		int a=in,b=in;
		a^=ans;b^=ans;
		ans=query(a,b);
		printf("%d\n",ans);
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值