codeforces 592D(树上的思路题)

题意:

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值