给出一颗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; }