题目大意:

  一棵树 n个点 有m个点被标记 求经过所有被标记的点的最短路径的长度以及起点(如有多条输出编号最小的起点)。

思路:

  1.当且仅当一个点本身或其子树中有点被标记时该点在最短的路径上因此,可以将多余的点删去,得到新的一棵树。
  2.不难发现,新树上的边必定被经过一次或两次,而且当只经过一次的边的集合为树的直径时,路径最短。

反思:

  “如有多条输出编号最小的起点”是个坑点,要在最远的点中找出编号最小的当端点。

代码:

 #include<cstdio>
#define u v[i]
const int M=;
int s,a[M],v[M],hea[M],nex[M];
bool b[M]; int read()
{
int x=; char ch=getchar();
while (ch< || ch>) ch=getchar();
while (ch> && ch<) x=(x<<)+(x<<)+ch-,ch=getchar();
return x;
} void add(int x,int y) { v[++s]=y,nex[s]=hea[x],hea[x]=s; } void dfs(int x,int p)
{
for (int i=hea[x];i;i=nex[i])
if (u^p) dfs(u,x),b[x]|=b[u];
} void DFS(int x,int y,int p)
{
a[x]=y;
for (int i=hea[x];i;i=nex[i])
if (b[u] && u^p) DFS(u,y+,x);
} int main()
{
int n=read(),m=read(),i,x,y;
for (i=;i<n;++i) x=read(),y=read(),add(x,y),add(y,x);
for (i=b[y=read()]=;i<m;++i) b[read()]=;
for (dfs(y,),a[]=s=-,i=;i<=n;++i) s=s+b[i];
for (DFS(y,x=,),i=;i<=n;++i)
if (b[i] && a[x]<a[i]) x=i;
for (DFS(x,y=,),i=;i<=n;++i)
if (b[i] && a[y]<a[i]) y=i;
printf("%d\n%d\n",x<y?x:y,s+s-a[y]);
return ;
}
05-13 21:25