【NOIP 模拟题】[T3] 约会(lca)
【题解】【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误终生...]
既然无能更改,又何必枉自寻烦忧