题意:
给出一颗n(n<=123456)个点的树,边权都为1,树上有给定的m个点被标记,问从树上一点出发,遍历所有被标记点的最短路程(不需要回到起始点)。
分析:
首先我们来看一下,要把这m个点联通需要的最小联通子树的边是必须遍历到的,从其他不在该子树上的点出发,这些边也必须遍历到,那么从子树上一最远点出发最优。
因为从一点出发的答案为 遍历到的边数*2 - 从该点出发的做能到达的最远点所需边数。
所以从该公式可以很明显发现,在最小联通子树上的一个最远点出发最优。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <vector>
#include <map>
#define rep(i,n) for(int i=0;i<(int)n;i++)
#define rep1(i,x,y) for(int i=x;i<=(int)y;i++)
typedef long long ll;
using namespace std;
const int N = 123456 + 100;
struct node{
int from,to;
node(int x=0,int y=0):from(x),to(y){}
};
vector<node> E;
vector<int> G[N];
int n,m,cnt[N]={0},mark[N]={0};
bool val[N]={0};
typedef pair<int,int> pii;
map<pii,int> M;
int all = 0;
void init_dfs(int u,int fa){
cnt[u] = val[u];
rep(i,G[u].size()){
int v = E[G[u][i]].to;
if(v == fa) continue;
init_dfs(v , u);
cnt[u]+=cnt[v];
if(cnt[v]){
M[pii(u,v)]=M[pii(v,u)]=1,all++;
}
}
}
int d[N];
int get_dis(int u,int fa){
d[u] = 0;
rep(i,G[u].size()){
int v = E[G[u][i]].to;
if(v == fa || !M[pii(u,v)]) continue;
d[u] = max(d[u], get_dis(v,u)+1);
}
return d[u];
}
int get_far(int u,int fa){
int ok = 0 , best = -1;
rep(i,G[u].size()){
int v = E[G[u][i]].to;
if(v == fa || !M[pii(u,v)]) continue;
ok++;
if(d[v] == d[u]-1){
int tem = get_far(v,u);
if(best == -1 || best > tem) best = tem;
}
}
if(!ok) return u;
return best;
}
int main()
{
scanf("%d %d",&n,&m);
int x,y;
rep1(i,1,n-1) {
scanf("%d %d",&x,&y); E.push_back(node(x,y)); E.push_back(node(y,x));
G[x].push_back(E.size()-2);
G[y].push_back(E.size()-1);
}
rep1(i,1,m) scanf("%d",&x),val[x]=true;
init_dfs(x , -1);
get_dis(x,-1);
int fir = get_far(x, -1);
get_dis(fir,-1);
int sec = get_far(fir,-1);
printf("%d\n%d\n",min(fir,sec),all*2-d[fir]);
return 0;
}