BZOJ3124 直径

Description:

求树的直径的必须边

 

思路:求出任意一条直径,然后对这条直径的每个点进行DFS(不过直径任何点),定义一个l,r,初始为直径的两端,然后计算出该点能到的最远距离如果等于其左端或右端,那么l = pos 或者 r = pos。

注意因为是从右向左搜的,所以左边的点只要收一次,且越早越好。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N = 1e6 + 10;

int head[N], now = 1;
struct edges{
    int to, next, w;
}edge[N<<1];
void add(int u, int v, int w){ edge[++now] = {v, head[u], w}; head[u] = now;}

int n, m, pre[N], tot, pos1, pos2;
bool isd[N];
long long dep[N], mx;
void dfs1(int x){
    for(int i = head[x]; i; i = edge[i].next){
        int v = edge[i].to;
        if(pre[x] == v) continue;
        pre[v] = x;
        dep[v] = dep[x] + edge[i].w;
        dfs1(v);
    }
}
void dfs(int x, int fa){
    for(int i = head[x]; i; i = edge[i].next){
        int v = edge[i].to;
        if(v == fa || isd[v]) continue;
        dep[v] = dep[x] + edge[i].w;
        mx = max(dep[v], mx);
        dfs(v, x);
    }
}
int main(){
    scanf("%d", &n);
    for(int i = 1; i < n; i++){
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        add(x, y, z); add(y, x, z);
    }
    dfs1(1);
    mx = 0;
    for(int i = 1; i <= n; i++)
      if(dep[i] > mx) mx = dep[i], pos1 = i;
    memset(dep, 0, sizeof(dep));
    memset(pre, 0, sizeof(pre));
    dfs1(pos1);
    mx = 0;
    for(int i = 1; i <= n; i++)
      if(dep[i] > mx) mx = dep[i], pos2 = i;
    for(int i = pos2; i; i = pre[i])
      isd[i] = 1;
    bool flag = 0;
    int l = pos1, r = pos2;
    long long ans = dep[pos2] - dep[pos1];
    for(int i = pre[pos2]; i != pos1; i = pre[i]){
        dfs(i, -1);
        int ls = dep[i], rs = dep[pos2] - dep[i];
        dep[i] = mx = 0;
        dfs(i, -1);
        if(mx == rs) r = i;
        if(mx == ls && !flag) 
          flag = 1, l = i;
    }
    for(int i = r; i != l; i = pre[i]) tot++; 
    printf("%lld\n%d\n",ans, tot);
    return 0;
}
View Code
posted @ 2018-04-05 23:20  Ror_shach  阅读(151)  评论(0编辑  收藏  举报