A-meeting 2019牛客暑期多校第四场 (树的直径)

题意:

给你 N 个点以及 N-1 条边将所有点两两连接(树),每个点都有对应编号。现在给你k个点,你要求出这个树上的某个点使得 所给两个点到该点的时间(经过的边数)其中最大的那个为 所能得到的所有答案的最小值。

思路

如果仅仅是两个点,那么我们用dijkstra跑一下就可以得到。但是这是k点,切要使这些点能达到的时间最小,我们就会想到树的直径这个概念。

树的直径
给定一棵树,树中每条边都有一个权值,树中两点之间的距离定义为连接两点的路径边权之和。树中最远的两个节点之间的距离被称为树的直径,连接这两点的路径被称为树的最长链。后者通常也可称为直径。

所以我们就想尽可能的去找这k个点所对应的树的直径,(可以想象一下你有一棵树两个点在树上随机分布,你肯定希望尽量沿着主干走,而不是把多余重复的去走分枝)

所以我们就利用求树直径的方法:(1)树形dp (2) 两次dfs或者bfs        (这道题选后者比较容易

所以我们就随便选一个已知点作为起点,第一次找到最远的点,再dfs第二次求得最远路径再除以2即为所求。 所以我们用spfa跑一下最远距离的点再跑回来即是最后的直径(跑最短路的算法都可以)

code:

#include<bits/stdc++.h>
using namespace std;
#define accept 0
const int maxm = 1e6+5;
const int maxn = 1e5+5;
const int inf = 0xffffff;
struct edge{
    int from;
    int to;
    int w;
    int next;
}e[maxm];
int pos[maxn],ans,far;
 
int head[maxn];
int vis[maxn];
int dist[maxn];
int n,m,top;
  
void add(int u,int v,int w){
    e[top].from = u;
    e[top].to = v;
    e[top].w = w;
    e[top].next = head[u];
    head[u] = top++;
}
void spfa(int s){
    queue<int> q;
    for(int i = 1; i <= n ; i++)
        dist[i] = inf;
    memset(vis,false,sizeof(vis));
    q.push(s);
    dist[s] = 0;
    while(!q.empty()){
        int u  = q.front() ;
        q.pop();
        vis[u] = false ;
        for(int i = head[u] ; ~i ; i = e[i].next){
            int v = e[i].to;
            if(dist[v] > dist[u] + e[i].w){
                dist[v] = dist[u] + e[i].w;
                if(!vis[v]){
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    ans = 0;
    for(int i=1;i<=m;i++){
        if(ans < dist[pos[i]]){
            ans = dist[pos[i]];
            far = i;
        }
    }
}
 
void init(){
    memset(head,-1,sizeof(head));
    top  =0;
}
 
int main(){
    init();
    scanf("%d %d",&n,&m);
    for(int i=1;i<n;i++){
        int u,v,w;
        scanf("%d %d",&u,&v);
        add(u,v,1);
        add(v,u,1);
    }
    int x;
    for(int i=1;i<=m;i++){
        scanf("%d",&pos[i]);
    }
    spfa(pos[1]);
    spfa(pos[far]);
    ans=(ans%2)?(ans/2+1):ans/2;
    printf("%d\n",ans);
    return accept;
}

 

posted @ 2019-08-24 12:19  Tianwell  阅读(170)  评论(0编辑  收藏  举报