不错的题。
LCA+并查集
每一个节点记录一个f[i]表示它到根的路径上第一个没经过的城市
初始如果它是要访问的则f[i]=i,否则f[i]=fa[i]
每次从一个点开始走到下一个点,求出LCA,计算出路径
然后分别从两个点开始向上跳,直到深度小于LCA,把每次跳到的每个节点设置为访问过
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#define maxn 500010
using namespace std;
int head[maxn],to[2*maxn],next[2*maxn],dep[maxn],fa[maxn][20];
int a[maxn],f[maxn];
bool vis[maxn];
int n,m,num,t;
void addedge(int x,int y)
{
num++;to[num]=y;next[num]=head[x];head[x]=num;
}
void dfs(int x)
{
if (vis[x]) f[x]=fa[x][0]; else f[x]=x;
for (int p=head[x];p;p=next[p])
if (to[p]!=fa[x][0])
{
fa[to[p]][0]=x;dep[to[p]]=dep[x]+1;
dfs(to[p]);
}
}
int find(int x)
{
if (f[x]==x) return x;
else return f[x]=find(f[x]);
}
void Up(int x,int z)
{
if (dep[x]<dep[z]) return;
vis[x]=1;f[x]=fa[x][0];
Up(find(fa[x][0]),z);
}
int go_up(int x,int d)
{
for (int i=0;i<=19;i++)
if (d&(1<<i)) x=fa[x][i];
return x;
}
int LCA(int x,int y)
{
if (dep[x]>dep[y]) x=go_up(x,dep[x]-dep[y]);
else y=go_up(y,dep[y]-dep[x]);
if (x==y) return x;
for (int i=19;i>=0;i--)
if (fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int main()
{
scanf("%d%d%d",&n,&m,&t);
for (int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
addedge(x,y);addedge(y,x);
}
for (int i=1;i<=n;i++) vis[i]=1;
for (int i=1;i<=m;i++) {scanf("%d",&a[i]);vis[a[i]]=0;}
dep[1]=1;
dfs(1);
for (int j=1;j<=19;j++)
for (int i=1;i<=n;i++)
fa[i][j]=fa[fa[i][j-1]][j-1];
long long ans=0;
for (int i=1;i<=m;i++)
if (!vis[a[i]])
{
int z=LCA(t,a[i]);
ans+=dep[t]+dep[a[i]]-2*dep[z];
Up(t,z);Up(a[i],z);
t=a[i];
}
printf("%lld\n",ans);
return 0;
}