前言:
这个每日一题对我来说稍微复杂了亿点点...
思路:
首先的题目的条件就是所有点的lca到所有点的路径都被标记了.我们要求点V到这些点集的一个最小距离.
假如这个点集的LCA和V的lca不是LCA的话,那么显然的一个结论距离就是V到lca的距离.
假如不是,那么V一定位于LCA的子树中.这是我们将树的dfs序求出,然后就简单了许多.距离就是要么是dfs序大于它的第一个点,要么就是小于它的第一个点.然后求个lca即可.
(看上去好像很简单...其实确实不难.)
代码:
#include <bits/stdc++.h>
using namespace std;
const int N=5e5+50,M=20;
vector<int>g[N],q[N];
vector<int>x;
int f[N][M],dep[N],dfn[N],id,top[N];
void dfs(int u,int fa)
{
dfn[u]=++id;dep[u]+=dep[fa]+1;
f[u][0]=fa;for(int i=1;i<20;i++) f[u][i]=f[f[u][i-1]][i-1];
for(int v:g[u])
if(v!=fa) dfs(v,u);
}
int lca(int u,int v)
{
if(dep[v]>dep[u]) swap(u,v);//假定u是dep大的.
for(int i=19;i>=0;i--)
{
if(dep[f[u][i]]>=dep[v]) u=f[u][i];
}if(u==v) return v;
for(int i=19;i>=0;i--)
{
if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
}return f[u][0];
}
int dis(int u,int v) {return dep[u]+dep[v]-2*dep[lca(u,v)];}
bool cmp(int a,int b) {return dfn[a]<dfn[b];}
int main()
{
int n;scanf("%d",&n);
for(int i=1;i<n;i++)
{
int u,v;scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}dfs(1,1);int t;scanf("%d",&t);
for(int i=1;i<=t;i++)
{
int u,v;scanf("%d",&u);
for(int j=0;j<u;j++)
{
scanf("%d",&v);q[i].push_back(v);
if(j==0) top[i]=v;
else top[i]=lca(top[i],v);
}sort(q[i].begin(),q[i].end(),cmp);
}int Q;scanf("%d",&Q);
while(Q--)
{
x.clear();int V,LCA;scanf("%d",&V);int u,v;scanf("%d",&u);
for(int i=0;i<u;i++)
{
scanf("%d",&v);x.push_back(v);
if(i==0) LCA=top[v];
else LCA=lca(LCA,top[v]);
}if(LCA!=lca(LCA,V)) printf("%d\n",dis(V,LCA));
else
{
int ans=2e9;
for(int e:x)
{
int sz=q[e].size();int pos=sz,r=sz-1,l=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(dfn[q[e][mid]]>=dfn[V]) pos=mid,r=mid-1;
else l=mid+1;
}
if(pos!=0) ans=min(ans,dis(V,lca(q[e][pos-1],V)));
if(pos!=sz) ans=min(ans,dis(V,lca(q[e][pos],V)));
}printf("%d\n",ans);
}
}
return 0;
}
京公网安备 11010502036488号