CS 400 Root LCA Queries 思维+LCA

题意:n个节点的树,Q次询问,每次询问(a,b,c)问有有多少个D,使得以D为根时,LCA(a,b)=c
1<=n,Q<=1e5


树上任意两点只有唯一路径,a->b  = a->LCA(a,b)->b 
若c没有出现在a-b的路径中 则lca(a,b)不可能为c.

判断c是否在a-b路径中 只要知道dist(a,c)+dist(c,b)是否等于dist(a,b).

若c在a-b路径中 那么满足条件的d的个数就是以c为根时(除了a,b子树的那部分).

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5,M=20;
int n,Q,u,v,dep[N],sz[N],par[N][M];
int st[N],ed[N],t=0;
vector<int> e[N];
void dfs(int u,int fa,int d)
{
	st[u]=t++;
	dep[u]=d,sz[u]=1;
	par[u][0]=fa;
	for(int i=1;i<M;i++)
		par[u][i]=par[par[u][i-1]][i-1];
	for(int i=0;i<e[u].size();i++)
	{
		int v=e[u][i];
		if(v==fa)	continue;
		dfs(v,u,d+1);
		sz[u]+=sz[v];
	}
	ed[u]=t;
}
int lca(int a,int b)
{
	if(dep[a]>dep[b])
		swap(a,b);
	if(dep[a]<dep[b])
	{
		int del=dep[b]-dep[a];
		for(int i=0;i<M;i++)
			if((del>>i)&1)
				b=par[b][i];
	}
	if(a!=b)
	{
		for(int i=M-1;i>=0;i--)
			if(par[a][i]!=par[b][i])
				a=par[a][i],b=par[b][i];
		a=par[a][0];
	}
	return a;
}
int dist(int a,int b)
{
	return dep[a]+dep[b]-2*dep[lca(a,b)];
}
bool isin(int a,int c)
{
	return st[c]<=st[a]&&ed[c]>=ed[a];
}
int move(int a,int c)
{
	if(isin(a,c))//a为c的子树. 
	{
		for(int i=M-1;i>=0;i--)
			if(dep[par[a][i]]>dep[c])
				a=par[a][i];	
		return a;
	}
	return par[c][0];
}
int main()
{
	cin>>n>>Q;
	for(int i=1;i<=n-1;i++)
	{
		cin>>u>>v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	dfs(1,0,1); 
	int a,b,c;
	while(Q--)
	{
		int res=n;
		cin>>a>>b>>c;
		if(dist(a,c)+dist(c,b)==dist(a,b))
		{
			a=move(a,c);
			b=move(b,c);
			if(a==par[c][0])		res=res-(n-sz[c]);
			else	res-=sz[a];
			if(b==par[c][0])	res=res-(n-sz[c]);
			else 	res-=sz[b];			
		}
		else
			res=0;
		printf("%d\n",res);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值