牛客OI周賽15-提高組 A環球旅行

題意

一顆有n(n<=106)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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章