【JZOJ B组】删边

Description

  给出N个点,N-1条边的连通图.
  现要求删除一条边,使得连通块的直径总和最大.所谓连通块的直径是指连通块中最远两点之间的距离。
     问:直径总和最大是多少?

Input

  文件名为 delete.in
  第一行正整数N.
  接下来N-1行.每行两个数,A,B,LEN表示A,B(1<=A,B<=N)有一条长度为Len(1<=Len<=1000)的边连接着.

Output

  文件名为 delete.out
  一个数Ans.直径总和的最大值.

Sample Input

10
2 1 982
3 1 169
4 1 934
5 1 325
6 1 735
7 1 675
8 2 302
9 3 450
10 5 173

Sample Output

2668

Hint

【数据范围】
  30% N<=100
  70% N<=5000
  100% N<=100000

思路

70%

直接暴力,O(n^2)

100%

首先,我们要预处理出每一棵子树的直径
一棵以 x 为根节点的
树的直径可能有两种情况:

  1. 经过 x;
  2. 不经过 x,即在某一棵子树上

为了第一种,我们要记录在每一棵子树中距离根节点
最远的点和次远的点所在的子树(a,b)以及这两个点离根节点的距离(d1,d2),同时要保证这两个点来自不同的子树。
那么一棵树的直径就是 max{每一棵子树的直径,d1+d2}。

预处理求出每一棵子树的直径后我们就可以递归求删去一条边后两个联通块的直径和。

那么现在的重点是求出上面的联通块的直径,这个直径有四种情况

第一种:递归的上一层,即删去(y,z)边时上面的联通块的直径,所以递归的时候需要记录一下当前当面联通块的直径

第二种情况是 y 的子树上距离 y 最远的那个点 d 到 y 的距离(d不是以 z 为根节点的树上)加上联通块中不是 y 的子树的部分距离 y最远的那个点 e 到 y 的距离,前者在预处理的时候已经记录了,后者递归的时候记录一下。

第三种情况是 y 的子树上(不含 z 子树的部分)子树中距离根节点最远的点和次远的点离 y 的距离之和。这个预处理的时候也已经记录了。

第四种情况就是 y 的所有子树(不包括 z)的最大的直径,这个预处理的时候也要记录一下!

总结一下预处理对于每一个点要记录的东西有:
1、以这个点为根的树中距离根节点最远、次远、第三远的点所在的子树以及这些点离根节点的距离。
2、以这个点为根的树中的子树直径最大的两个以及它们的直径值。
3、以这个点为根的树的直径(当然,这个可以由 1、2 得到)。

代码

#include
#include
#include
using namespace std;
const int maxn=100077;
int g[maxn][5],mf[maxn][5],id[maxn][5],f[maxn],n,cnt=0,ass=0,list[maxn*2],iid[maxn][5];
struct E
{
    int to,next,v;
}e[maxn*2];
void add(int u,int v,int val)
{
    e[++cnt].to=v; e[cnt].next=list[u]; list[u]=cnt; e[cnt].v=val;
}
void dfs(int u,int fa)
{
    for(int i=list[u]; i; i=e[i].next) if(e[i].to!=fa)
    {
        int x=e[i].to;
        dfs(x,u);
        f[u]=max(f[u],f[x]);
        if(f[x]>mf[u][1])
        {
            mf[u][2]=mf[u][1];
            mf[u][1]=f[x];
//          iid[u][2]=iid[u][1]
            iid[u][1]=x;
        }else
        if(f[x]>mf[u][2]) mf[u][2]=f[x];
        int d=g[x][1]+e[i].v;
        if(d>g[u][1])
        {
            g[u][3]=g[u][2]; g[u][2]=g[u][1]; g[u][1]=d;
            id[u][3]=id[u][2]; id[u][2]=id[u][1]; id[u][1]=x;
        }else
        if(d>g[u][2])
        {
            g[u][3]=g[u][2]; g[u][2]=d;
            id[u][3]=id[u][2]; id[u][2]=x;
        }else
        if(d>g[u][3])
        {
            g[u][3]=d;
            id[u][3]=x;
        }
    }
    f[u]=max(g[u][1]+g[u][2],mf[u][1]);
}
void work(int u,int d,int r,int fa)
{
    if(u!=1) ass=max(ass,f[u]+d);
    int t1,t2,t3;
    for(int i=list[u]; i; i=e[i].next) if(e[i].to!=fa)
    {
        int x=e[i].to;
        if(x==id[u][1])
        {
            t1=g[u][2];
            t2=g[u][2]+g[u][3];
        }else
        {
            t1=g[u][1];
            if(x==id[u][2]) t2=g[u][1]+g[u][3];
            else t2=g[u][1]+g[u][2];
        }
        if(x==iid[u][1]) t3=mf[u][2];else t3=mf[u][1];
        work(x,max(max(d,t3),max(t1+r,t2)),max(r,t1)+e[i].v,u);
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1; i<=n-1; i++)
    {
        int x,y,v;
        scanf("%d%d%d",&x,&y,&v);
        add(x,y,v); add(y,x,v);
    }
    dfs(1,0);
    work(1,0,0,0);
    printf("%d",ass);
}

你可能感兴趣的:(题解,树形DP)