牛客OI周赛15-提高组 A环球旅行

题意

一颗有 n ( n < = 1 0 6 ) n(n<=10^6) 个结点的数树,每条边有w的花费,可以随意删除一条边,删除之后剩下2棵树,要使这2棵树其中最大的2点距离 最小

思路

首先肯定是删除原树直径中的点,因为删去其他边,那么最长的还是直径,所以删去直径的边才有可能减少距离,可以先把直径起始点s、t 找出来,然后依次枚举删去直径上的边之后剩余2颗子树的最长路径。
在这里插入图片描述
在这里插入图片描述
分别以s和t为根维护一个dp[u]表示以u为根的数里面的最长路径,然后删除u,v连着的边的时候我们就可以直接通过dp[u](左)和 dp[v] (右) 来更新答案.
vector会被卡建图,链式前向星ok的

code

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int man = 1e6+10;
#define IOS ios::sync_with_stdio(0)
template <typename T>
inline T read(){T sum=0,fl=1;int ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')fl=-1;
for(;isdigit(ch);ch=getchar())sum=sum*10+ch-'0';
return sum*fl;}
template <typename T>
inline void write(T x) {static int sta[35];int top=0;
do{sta[top++]= x % 10, x /= 10;}while(x);
while (top) putchar(sta[--top] + 48);}
template<typename T>T gcd(T a, T b) {return b==0?a:gcd(b, a%b);}
template<typename T>T exgcd(T a,T b,T &g,T &x,T &y){if(!b){g = a,x = 1,y = 0;}
else {exgcd(b,a%b,g,y,x);y -= x*(a/b);}}
#ifndef ONLINE_JUDGE
#define debug(fmt, ...) {printf("debug ");printf(fmt,##__VA_ARGS__);puts("");}
#else
#define debug(fmt, ...)
#endif
typedef long long ll;
const ll mod = 1e9+7;
struct edg{
    int to,val,next;
}e[man*2];
int head[man],cnt;
int dp[man][2];//表示以u为根这颗树的最长路径
int dis[man],fa[man];
int dis_[man][2];//表示与u最远的链
int maxx,max_id,s,t;
 
void add(int u,int v,int w){
    e[++cnt].next = head[u];
    e[cnt].to = v;
    e[cnt].val = w;
    head[u] = cnt;
}

void dfs1(int u,int f){//处理出直径的端点
    for(int i = head[u];~i;i = e[i].next){
        int v = e[i].to;
        if(v==f)continue;
        dis[v] = dis[u] + e[i].val;
        if(dis[v]>maxx){
            maxx = dis[v];
            max_id = v;
        }
        dfs1(v,u);
    }
}
 
void dfs(int u,int f,int op){
    fa[u] = f;
    int maxx1 = 0,maxx2 = 0;
    dp[u][op] = dis_[u][op] = 0;
    for(int i = head[u];~i;i = e[i].next){
        int v = e[i].to,w = e[i].val;
        if(v==f)continue;
        dfs(v,u,op);
        int tp = dis_[v][op]+w;
        dis_[u][op] = max(dis_[u][op],tp);
        if(tp>maxx1){//维护2个最长链和次长链
            maxx2 = maxx1;
            maxx1 = tp;
        }else if(tp>maxx2)maxx2 = tp;
        dp[u][op] = max(dp[u][op],dp[v][op]);//也许这颗树的最长路径不经过根节点
    }
    dp[u][op] = max(dp[u][op] , maxx1 + maxx2);//最长路径经过根节点。
}
 
int main() {
    #ifndef ONLINE_JUDGE
        //freopen("in.txt", "r", stdin);
        //freopen("out.txt","w",stdout);
    #endif
    int n;
    cin >> n;
    memset(head,-1,sizeof(head));
    for(int i = 1;i < n;i++){
        int u,v,w;
        cin >> u >> v >> w;
        add(u,v,w);
        add(v,u,w);
    }
    maxx = -1,max_id = 1;
    dis[1] = 0;
    dfs1(1,0);
    maxx = -1;
    s = max_id;
    dis[s] = 0;
    dfs1(max_id,0);
    t = max_id;
    //找出s,t
    dfs(t,0,0);
    dfs(s,0,1);
    //s为根,t为根求一遍dp
    int ans = INT_MAX;
    while(t!=s){//枚举直径的边。
        ans = min(ans, max(dp[fa[t]][0],dp[t][1]));
        t = fa[t];
    }
    cout << ans << endl;
    return 0;
}
原创文章 93 获赞 12 访问量 8999

猜你喜欢

转载自blog.csdn.net/weixin_43571920/article/details/105346498