【NOIP 模拟题】[T3] 约会(lca)

35 篇文章 0 订阅
33 篇文章 0 订阅


【题解】【lca】

【一眼lca,然后、然后各种漏情况。。。】

【通过分析题目,我们要先找使到两个点的距离相等的在它们最短路径上的那一点grf。这样,我们就要先通过找两个点的lca,求出两个点之间的最短路径len,并判断grf的大致位置】

[Part 1]【如果两个点的深度相同,那么,grf必然是它们的lca,那么剩下的可能的点就是所有能跟grf相连且到达grf不经过两个起点的点,就是size[grf]-size[s1]-size[s2],即除了以s1、s2为根的两棵子树中的点,剩下的都是满足条件的点】

[Part 2]【如果两个点之间的距离为奇数那么满足条件的点不存在】

[Part 3]【由深度较深的一个点son向上蹦len/2层,可以用类似lca的倍增方法做。然后,满足条件的答案就在grf的子树中,并且要除去以son为根的子树里的点】

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[200010],nxt[200010],p[100010],tot;
int father[100010],f[100010][20],size[100010],dep[100010]; 
int n,m,mi[20];
inline void add(int x,int y)
{
	tot++; a[tot]=y; nxt[tot]=p[x]; p[x]=tot;
	tot++; a[tot]=x; nxt[tot]=p[y]; p[y]=tot;
}
void build(int x,int fa,int h)
{
	dep[x]=h; size[x]=1; father[x]=fa;
	for(int i=1;i<17;++i) f[x][i]=f[f[x][i-1]][i-1];
	for(int i=p[x];i!=-1;i=nxt[i])
	 if(a[i]!=fa)
	  {
	  	f[a[i]][0]=x;
	  	build(a[i],x,h+1);
	  	size[x]+=size[a[i]];
	  }
}
inline int lca(int x,int y)
{
	int xx=x,yy=y;
	if(x==y) return n;
	int gf;
	if(dep[x]<dep[y]) swap(x,y);
	if(dep[y])
	 {
	 	int k=dep[x]-dep[y];
	 	for(int i=0;i<17;++i)
	     if(k>>i&1) x=f[x][i];
	    if(x!=y) 
	     {
	 	    for(int i=16;i>=0;--i)
	         if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	        gf=f[x][0];
	      }
	    else gf=x;
	  }
	 else gf=y;
	int l1=dep[xx]-dep[gf],l2=dep[yy]-dep[gf];
	int len=l1+l2;
	if(l1==l2) return n-size[x]-size[y];
	if(len%2) return 0;
	len/=2;
	int grf,gs;
	int son;
	if(l1<l2) son=yy;
	 else son=xx;
	int high=dep[son]-len;
	grf=son;
    for(int i=16;i>=0;--i) 
     if(dep[f[grf][i]]>high) gs=grf,grf=f[grf][i];
    gs=grf; grf=f[grf][0];
	return size[grf]-size[gs];
}
int main()
{
	freopen("date.in","r",stdin);
	freopen("date.out","w",stdout);
	int i,j;
	memset(p,-1,sizeof(p));
	memset(nxt,-1,sizeof(nxt));
	mi[0]=1;
	for(i=1;i<=17;++i) mi[i]=mi[i-1]*2;
	scanf("%d",&n);
	for(i=1;i<n;++i)
	 {
	 	int x,y;
	 	scanf("%d%d",&x,&y);
	 	add(x,y); 
	  } 
	build(1,0,1);
	scanf("%d",&m);
	for(i=1;i<=m;++i)
	 {
	 	int x,y;
	 	scanf("%d%d",&x,&y);
	 	int ans=lca(x,y);
	 	printf("%d\n",ans);
	 }
	return 0;
}

[一用lower_bound误终生...]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值