【树上倍增】CF294 div2 E A and B and Lecture Rooms

虽然是道2100,但是思维难度好像也不是很高的样子....

Problem - 519E - Codeforces

题意:

给定一棵树和两个结点u和v,每次询问问整棵树上离u和v的距离一样的结点个数是多少

思路:

对u和v分类讨论即可

如果u==v,那么整棵树都满足条件

如果dep[u]==dep[v],那么如下图所示的结点x和结点y所在子树之外的所有结点都满足条件(图画的比较抽象qwq)

如果dep[u]!=dep[v]

先把u到v的路径中到u和v距离一样的点找出来:uu

vv是uu下面的那个结点

  满足条件的就是uu这棵子树除去vv这颗子树的大小

Code:

#include <bits/stdc++.h>

#define int long long

using namespace std;

const int mxn=1e5+10;
const int mxe=1e5+10;

struct ty{
	int to,next;
}edge[mxe<<2];

int N,M,u,v,tot=0;
int head[mxn];
int F[mxn][33],dep[mxn],sz[mxn];

void add(int u,int v){
	edge[tot].to=v;
	edge[tot].next=head[u];
	head[u]=tot++;
}
void G_init(){
	tot=0;
	for(int i=0;i<=N;i++){
		head[i]=-1;
	}
}
void dfs(int u,int fa){
	sz[u]=1;
	dep[u]=dep[fa]+1;
	F[u][0]=fa;
	for(int i=1;i<=30;i++) F[u][i]=F[F[u][i-1]][i-1];
	for(int i=head[u];~i;i=edge[i].next){
		if(edge[i].to==fa) continue;
		dfs(edge[i].to,u);
		sz[u]+=sz[edge[i].to];
	}
}
int lca(int u,int v){
	if(dep[u]<dep[v]) swap(u,v);
	for(int j=30;j>=0;j--){
		if(dep[F[u][j]]>=dep[v]){
			u=F[u][j];
		}
	}
	if(u==v) return u;
	for(int j=30;j>=0;j--){
		if(F[u][j]!=F[v][j]){
			u=F[u][j];
			v=F[v][j];
		}
	}
	return F[u][0];
}
void solve(){
	cin>>N;
	G_init();
	for(int i=1;i<=N-1;i++){
		cin>>u>>v;
		add(u,v);
		add(v,u);
	}
	dfs(1,0);
	cin>>M;
	for(int i=1;i<=M;i++){
		cin>>u>>v;
		if(u==v){
			cout<<N<<'\n';
			continue;
		}
		int LCA=lca(u,v);
		if(dep[u]==dep[v]){
			int dis=dep[u]-dep[LCA];
			dis--;
			int uu=u;
			for(int j=30;j>=0;j--){
				if((dis>>j)&1){
					uu=F[uu][j];
				}
			}
			int vv=v;
			for(int j=30;j>=0;j--){
				if((dis>>j)&1){
					vv=F[vv][j];
				}
			}
			cout<<N-sz[uu]-sz[vv]<<'\n';
		}else{
			if(dep[u]<dep[v]) swap(u,v);
			int d1=dep[u]-dep[LCA];
			int d2=dep[v]-dep[LCA];
			if(abs(d1-d2)&1){
				cout<<0<<'\n';
			}else{
				int dis=(d1+d2)/2;
				int uu=u;
				for(int j=30;j>=0;j--){
					if((dis>>j)&1){
						uu=F[uu][j];
					}
				}
				int vv=u;
				dis--;
				for(int j=30;j>=0;j--){
					if((dis>>j)&1){
						vv=F[vv][j];
					}
				}
				cout<<sz[uu]-sz[vv]<<'\n';
			}
		}
	}
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int __=1;//cin>>__;
	while(__--)solve();return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值