维修道路 题解

[题意]

一棵n个节点的树,求两条路径s1,s2最大的长度积,满足s1,s2都是两点之间的最短路径,而且两条路径不含任何公共点.(n<=3000).

[思路]

     O(n^2):

         枚举树上的每条边,把此边从树上移除,那么原来的树就变成了两棵子树:所求的两条路径分别在两棵子树中.那么为了满足题意中的最大长度积,分别求出两棵子树的直径即可.

     O(n):

         通过模拟(应该有别的方法,反正我是通过模拟来猜测的)可以假想:两条路径一定有一条的端点是直径的某一个端点.

所有答案分为以下两种情况:

1.两条路径的某个端点分别是直径的两个端点.

2.其中一条路径就是直径.

明确了两种情况就可以枚举+贪心,预处理出直径上每个点内最深的深度和不经过直径上的任意点的最长路径就可以完成O(n).


<span style="font-size:18px;">#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
inline void rd(int &res){
    res=0;char c;
    while(c=getchar(),c<48);
    do{
        res=(res<<1)+(res<<3)+(c^48);
    }while(c=getchar(),c>=48);
}
const int M=3005;
int fa[M],c=0,n,id[M],dm[M],ID1,ID2,mx=0,tot=0,val[M],tmp[M];
vector<int>e[M];
void dfs(int x,int f,int d){
    if(c)fa[x]=f;
    if(d>=mx){
        mx=d;ID2=x;
    }
    for(int i=0;i<e[x].size();i++){
        int y=e[x][i];
        if(y==f||id[y])continue;
        dfs(y,x,d+1);
    }
}
void idfs(int x,int f,int d){
    val[id[x]]=max(val[id[x]],d);
    for(int i=0;i<e[x].size();i++){
        int y=e[x][i];
        if(y==f||id[y])continue;
        id[y]=id[x];
        idfs(y,x,d+1);
    }
}
int solve(int x){
    int res=0;
    for(int i=0;i<e[x].size();i++){
        int y=e[x][i];
        if(y==fa[x]||id[y])continue;
        mx=0;
        dfs(y,x,0);
        dfs(ID2,ID2,0);
        res=max(mx,res);
    }
    return res;
}
int main(){
    int i,j,k,a,b,ans=0,mxd=0;
    scanf("%d",&n);
    for(i=1;i<n;i++){
        rd(a);rd(b);
        e[a].push_back(b);
        e[b].push_back(a);
    }
    dfs(1,0,0);
    ID1=ID2;c=1;
    dfs(ID1,0,0);c=0;
    int x=ID2;
    while(x!=0){
        id[x]=++tot;
        dm[tot]=solve(x);
        mxd=max(mxd,dm[tot]);//不经过直径的最长路径
        idfs(x,fa[x],0);
        x=fa[x];//遍历直径.
    }
    for(i=tot;i>=1;i--)tmp[i]=max(tmp[i+1],tot-i+val[i]);//val[i]表示直径上编号为i的点下的最大深度.
    for(i=1;i<=tot;i++)ans=max(ans,(val[i]+i-1)*tmp[i+1]);
    ans=max(ans,(tot-1)*mxd);
    printf("%d\n",ans);
    return 0;
}
/**************************************************************
    Problem: 3006
    User: LIN452
    Language: C++
    Result: 正确
    Time:0 ms
    Memory:1856 kb
****************************************************************/</span>


20160625 离线赛/CodeForces


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值