3246: [Ioi2013]Dreaming
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 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
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
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)的