BZOJ3124: [Sdoi2013]直径 (树形DP)

题意:给一颗树 第一问求直径 第二问求有多少条边是所有直径都含有的

题解:求直径就不说了 解第二问需要自己摸索出一些性质

   任意记录一条直径后 跑这条直径的每一个点

   如果以这个点不经过直径能到达最远的距离等于这个点到直径某端点的距离的话

   那么从这个点到直径这一端点的这一条链显然是不满足答案的

   于是我们可以设置两个端点l,r 维护能满足条件的地方

   最后答案就是r - l + 1个端点r - l条边

#include <stdio.h>
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long ll;

int n, rt, l, r, num;
int pre[200005];
int vis[200005];
int to[400005];
int val[400005];
int nex[400005];
int head[200005];
int lian[200005];
ll dis[200005];
ll diss[200005];
ll zd, ans1, ans2;

void dfs(int x, int fa)
{
    int c = head[x];
    for(int i = c; i; i = nex[i])
    {
        int v = to[i];
        if(v == fa) continue;
        dis[v] = dis[x] + (ll)val[i];
        if(dis[v] > zd) zd = dis[v], rt = v;
        pre[v] = x;
        dfs(v, x);
    }
}

void chain()
{
    int x = rt;
    while(x != pre[l])
    {
        num++;
        vis[x] = 1;
        lian[num] = x;
        x = pre[x];
    }
}

void dfs1(int x, int fa)
{
    int c = head[x];
    for(int i = c; i; i = nex[i])
    {
        int v = to[i];
        if(v == fa) continue;
        if(vis[v]) continue;
        diss[v] = diss[x] + (ll)val[i];
        if(diss[v] > zd) zd = diss[v];
        dfs1(v, x);
    }
}

int main()
{
    scanf("%d", &n);
    int cnt = 0; zd = 0; num = 0;
    for(int i = 1; i < n; i++)
    {
        int u, v, o; scanf("%d%d%d", &u, &v, &o);
        to[++cnt] = v; nex[cnt] = head[u]; head[u] = cnt; val[cnt] = o;
        to[++cnt] = u; nex[cnt] = head[v]; head[v] = cnt; val[cnt] = o;
    }
    dfs(1, -1);
    l = rt; dis[l] = 0; zd = 0;
    pre[l] = -1;
    dfs(l, -1);
    r = rt;
    ans1 = zd;
    chain();

    int a1 = 1, a2 = num;
    for(int i = 1; i <= num; i++)
    {
        zd = 0;
        dfs1(lian[i], -1);
        if(dis[r] - dis[lian[i]] == zd) a1 = i;
        if(zd == dis[lian[i]])
        {
            a2 = i;
            break;
        }
    }
    printf("%lld
%d
", ans1, a2 - a1);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/lwqq3/p/9183668.html