O(n^2)暴力枚举点然后算LCA显然是不可行的。
看到这个数据范围,考虑二分。我们二分枚举LCA的深度mid,然后把set A中每个点深度为mid的祖宗找出来,看set B中每个点深度为mid的祖宗有没有与set A刚才找到的祖宗重合,如果有重合,l=mid+1,否则r=mid-1。
记得多组数据。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
using namespace std;
#define int long long
const int N=1e5,LOGN=20;
int fa[N+10][20],head[N+10],ver[N*2+10],nxt[N*2+10],tot=0,dep[N+10];
void add(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
int getfa(int x,int k)
{
int ans=x,pos=0;
while((1<<pos)<=k)
{
if((1<<pos)&k) ans=fa[ans][pos];
pos++;
}
return ans;
}
bool vis[N+10];
int que[N+10],h=1,t=0,zzt;
void bfs()
{
que[++t]=1;
dep[1]=1;
while(h<=t)
{
int x=que[h]; h++;
// printf("x:%lld
",x);
vis[x]=1;
for(int i=head[x];i;i=nxt[i])
{
int y=ver[i];
if(vis[y]) continue;
fa[y][0]=x;
dep[y]=dep[x]+1;
// maxdep=max(maxdep,dep[y]);
for(int j=1;j<=zzt;j++) fa[y][j]=fa[fa[y][j-1]][j-1];
que[++t]=y;
}
}
}
vector<int> s1,s2;
int k1,k2;
int book[N+10];
int maxdep;
int bs(int pos) //binary search
{
int l=1,r=maxdep,ans=0;
while(l<=r)
{
int mid=(l+r)/2;
// printf("mid:%lld
",mid);
bool flag=0;
for(int i=0;i<k1;i++)
if(dep[s1[i]]>=mid)
book[getfa(s1[i],dep[s1[i]]-mid)]=pos;
for(int i=0;i<k2;i++)
if(dep[s2[i]]>=mid)
if(book[getfa(s2[i],dep[s2[i]]-mid)]==pos)
{
flag=1;
break;
}
if(flag)
{
l=mid+1;
ans=mid;
}
else r=mid-1;
}
return ans;
}
signed main()
{
int n,m;
while(~scanf("%lld %lld",&n,&m))
{
memset(book,0,sizeof(book));
h=1;t=0;
memset(vis,0,sizeof(vis));memset(head,0,sizeof(head));tot=0;
zzt=log2(n)+1;
for(int i=1;i<n;i++)
{
int u,v;
scanf("%lld %lld",&u,&v);
add(u,v);
add(v,u);
}
bfs();
for(int i=1;i<=m;i++)
{
maxdep=0;
s1.clear();s2.clear();
scanf("%lld",&k1);
for(int j=1;j<=k1;j++)
{
int x;
scanf("%lld",&x);
s1.push_back(x);
maxdep=max(maxdep,dep[x]);
}
scanf("%lld",&k2);
for(int j=1;j<=k2;j++)
{
int x;
scanf("%lld",&x);
s2.push_back(x);
maxdep=max(maxdep,dep[x]);
}
printf("%lld
",bs(i));
}
}
return 0;
}