3246: [Ioi2013]Dreaming

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 547  Solved: 205
[Submit][Status][Discuss]

Description

Serpent(水蛇)生活的地方有N个水坑,编号为0,...,N - 1,有M条双向小路连接这些水坑。每两个水坑之间至多有一条路径(路径包含一条或多条小路)相互连接,有些水坑之间根本无法互通(即M ≤ N-1 )。Serpent走过每条小路需要一个固定的天数,不同的小路需要的天数可能不同。Serpent的朋友袋鼠希望新修 N - M - 1条小路,让Serpent可以在任何两个水坑间游走。袋鼠可以在任意两个水坑之间修路,Serpent通过每条新路的时间都是L天。袋鼠希望找到一种修路方式使得修路之后Serpent在每两个水坑之间游走的最长时间最短。

举例说明

上图中有12个水坑8条小路( N = 12, M = 8)。假如L = 2 ,即Serpent通过任何一条新路都需要2天。那么,袋鼠可以修建3条新路: 
水坑1和水坑2之间;
水坑1和水坑6之间;
水坑4和水坑10之间。

上图显示了修路后的最终状态。从水坑0走到水坑11的时间最长,需要18天。这是 最佳结果,无论袋鼠如何选择修路方式,总会存在一些水坑对,Serpent需要18天 或者更长时间从其中一个走到另一个。
 

 

Input

N : 水坑的数目。
M : 原本存在的小路的数目。
L : Serpent通过新修的路经的时间。
A, B 和 T: 三个包含M个元素的数组,分别表示每条小路的两个端点和通过这条小路的时间。例如,第i条小路连接水坑 A[i-1]和水坑B[i-1],通过这条小路的时间是T[i-1]天。
 
 

Output


如上所述,表示游走于两个距离最远的水坑之间所需的时间。

Sample Input

12 8 2
0 8 4
8 2 2
2 7 4
5 11 3
5 1 7
1 3 1
1 9 5
10 6 3

Sample Output

18

HINT

 

 n <= 500000

 

Source

-----------------------------

贪心

先求出每个联通块的权值key

表示这个联通块里以每个点为根的子树中深度最大的最小值

具体来说就是 

int key(vector<int>&S){
    int Key=inf;
    for(i:S){
        dfs(i);
        if(maxdept<key)
            key=maxdept;
    }
    return Key;    
}

S是一个联通块

当然这样是O(n2)的

然后有一种高明一点的做法

先求出这个联通块的直径

然后想象一下这条直径从左到右摆在那里,并且每个点下面都挂了一棵子树

比如说这样

灵魂画家rwy

L[u]表示u到左端点的距离R[u]表示u到右端点的距离

然后直径上面的每个点的maxdeep就是 max(max(L[u],R[u]),maxdeep(u的子树));

这样直径上的每个点就可以一次O(n)求出了 然后非直径上的点显然不优于是就不求了

--------------------------

求出每个联通块的key有什么用呢

最后的答案一定是每个联通块连城一棵树。

口胡一下

每个联通块要向其他联通块连边的那个点一定是刚才要求的那个maxdeep最小的点

然后把那些点连成一个菊花树最优 菊花树的中心节点就是maxdeep最大的那个联通块的点

就像这样

蓝色的就是要添加的边

设这些联通块的最大值次大值第三大为 fir,sec,thi(如果有的话)

那么答案就是 max(fir+l+sec,l*2+sec+thi)

判一下只有一个联通块或两个联通块的情况

代码如下:

#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define CH c=getchar()
#define For(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
inline int read(){
    bool f=0;char CH;for(;!isdigit(c);CH)if(c=='-')f=1;
    int x=0;for(;isdigit(c);CH) x=(x<<1)+(x<<3)+c-48;return f?-x:x;
}
const int N = 500005;
int head[N],to[N<<1],nxt[N<<1],w[N<<1],cnt;
int dept[N],f[N],L[N],R[N],T[N];
bool vis[N];
int n,m,l;
void add(int u,int v,int W){
    nxt[++cnt]=head[u];head[u]=cnt;to[cnt]=v;w[cnt]=W;
    nxt[++cnt]=head[v];head[v]=cnt;to[cnt]=u;w[cnt]=W;
}
int s[N],r;
int mxd,V,U;
int key[N],O;
int ans;
void dfs(int u){
//  s.push_back(u);
    vis[u]=1;
    s[++r]=u;mxd=max(mxd,dept[u]);
    for(int i=head[u];i;i=nxt[i]){
        int v=to[i];if(vis[v]) continue;
        dept[v]=dept[u]+w[i];
        f[v]=u;T[v]=w[i];
        dfs(v);
    }
}
inline bool cmp(int a,int b){
    return a>b;
}
int main(){
    n=read();m=read();l=read();
    For(i,1,m){
        int u=read()+1,v=read()+1;int w=read();
        add(u,v,w);
    }
    For(i,1,n){
        if(vis[i]) continue;
        r=0;
        dfs(i);mxd=-1;f[i]=0;
        For(j,1,r){
            if(dept[s[j]]>mxd){
                V=s[j];mxd=dept[V];
            }   
            vis[s[j]]=0;
        }
        r=0;f[V]=0;dept[V]=0;
        dfs(V);mxd=-1; 
        For(j,1,r){
            if(dept[s[j]]>mxd){
                U=s[j];mxd=dept[U];
            }
            vis[s[j]]=0;
        }
        ans=max(ans,mxd);
        r=0;vis[U]=1;L[U]=mxd;
        for(int x=U;f[x];vis[x=f[x]]=1)
            R[f[x]]=R[x]+T[x],L[f[x]]=mxd-R[f[x]];
        int F=1e9;
        for(int x=U;x;x=f[x]){
            dept[x]=0;
            mxd=-1;dfs(x);
            F=min(F,max(mxd,max(R[x],L[x])));
        }
//      key.push_back(F);
        key[++O]=F;
    }
    sort(key+1,key+O+1,cmp);
    if(O>1){
        ans=max(ans,l+key[1]+key[2]);
        if(O>2)  
            ans=max(ans,l+l+key[2]+key[3]);
    }
    printf("%d",ans);
    return 0;
}

我太懒了所以直接sort了 当然可以做到O(N)的

 

 posted on 2017-04-06 22:14  rwy  阅读(337)  评论(0编辑  收藏  举报