CF519E A and B and Lecture Rooms

题意简述

题目链接

  给定一棵无根树,要求回答m次询问,每次询问给定两个点u,v,求树上与这两个点距离相等的点的个数。距离定义为树上两点间的边数。

算法概述

  这道题难度评定成紫色着实有点过了,个人感觉封顶蓝色。毕竟前置知识只有一个树上倍增,而且也没什么思维难度,就简单分类讨论一下就完事了。

  手动画一画样例不难发现,能够作为答案的点中,距离给定两点u,v最近的点,肯定在路径u→v上,且必然为路径的中点。对这个点分两种情况考虑:

  1. 这个点就是lca(u,v),则说明u,v深度相同。但u,v深度相同并不能直接说明这个点就是lca(u,v),因为还有一种特殊情况即u,v为同一点,特判即可,这种情况的答案显然是树上所有点都能走。然后对于这个点为lca(u,v)的情况,我们执行树上倍增的部分过程,让u,v同时上跳,跳到lca(u,v)的儿子x,y为止,那么答案即为n-size[x]-size[y],因为这个点可以走到除了这两棵子树以外的所有点,这些点都能成为答案。

  2. 这个点不是lca(u,v),那么u,v深度一定不同,不妨设u为深度大的点。假设路径中点为x,则v必然要经过lca(u,v)再向下走,才能走到x,而u只需上跳即可,设u跳到x的路上,经过的x的儿子节点为y。那么答案则需要将树上的这两部分剪除,即为size[x]-size[y]。跳的过程同样用倍增即可。

  时间复杂度O(mlogn)

参考代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1e5+10,MAX_LOG=17;
struct Edge{
	int to,next;
}edge[N<<1];int idx;
int h[N];

void add_edge(int u,int v){edge[++idx]={v,h[u]};h[u]=idx;}

int f[N][MAX_LOG];
int dep[N],siz[N];
int n,m;

void dfs(int p,int fa)
{
	dep[p]=dep[fa]+1;
	siz[p]=1;
	for(int i=h[p];~i;i=edge[i].next)
	{
		int to=edge[i].to;
		if(to==fa)continue;
		f[to][0]=p;
		for(int j=1;j<MAX_LOG;j++)
			f[to][j]=f[f[to][j-1]][j-1];
		dfs(to,p);
		siz[p]+=siz[to];
	}
}

inline int lca(int x,int y)
{
	if(dep[x]<dep[y])swap(x,y);
	int d=dep[x]-dep[y];
	for(int j=MAX_LOG-1;j>=0;j--)
		if(d>=(1<<j))d-=(1<<j),x=f[x][j];
	if(x==y)return x;
	for(int j=MAX_LOG-1;j>=0;j--)
		if(f[x][j]!=f[y][j])x=f[x][j],y=f[y][j];
	return f[x][0];
}

int main()
{
	scanf("%d",&n);
	memset(h,-1,sizeof h);
	for(int i=1;i<=n-1;i++)
	{
		int u,v;scanf("%d%d",&u,&v);
		add_edge(u,v);
		add_edge(v,u); 
	}
	dfs(1,0);
	scanf("%d",&m);
	while(m--)
	{
		int u,v;scanf("%d%d",&u,&v);
		if(dep[u]==dep[v])
		{
			if(u==v){printf("%d\n",n);continue;} 
			for(int j=MAX_LOG-1;j>=0;j--)
				if(f[u][j]!=f[v][j])u=f[u][j],v=f[v][j];
			printf("%d\n",n-siz[u]-siz[v]);
		}
		else
		{
			int d=dep[u]+dep[v]-2*dep[lca(u,v)];
			if(d%2){printf("0\n");continue;}
			d/=2;d--;
			if(dep[u]<dep[v])swap(u,v);
			for(int j=MAX_LOG-1;j>=0;j--)
				if(d>=(1<<j))d-=(1<<j),u=f[u][j];
			int x=f[u][0];
			printf("%d\n",siz[x]-siz[u]);
		}
	}
	return 0;
}

  

posted @ 2020-08-21 08:57  魑吻丶殇之玖梦  阅读(160)  评论(0编辑  收藏  举报