bzoj 3246 [Ioi2013] Dreaming 题解

来源:互联网 发布:剑倚天下宝器进阶数据 编辑:程序博客网 时间:2024/05/17 22:33

【原题】

3246: [Ioi2013]Dreaming

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 194  Solved: 68

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

【分析】话说此题坑了我一上午。许昊然的题解已经讲得很详细了。

http://www.ccf.org.cn/resources/1190201776262/fujian/xuhaoran2013-07-25-03_33_55.pdf

但是我还想补充几点:

①设树中一个点到其他点的最长距离为S,怎么快速求一棵树的S的最大值(即直径)和最小值。
对于某个点i,我们设max[i]表示它的最远儿子距离他的长度。再定义maxx[i]表示不经过它的max[i]经过的儿子的最远距离(也就是次远距离)。我们可以先做一遍树规,把这两个值预处理一下。
然后我们考虑第二遍DP时,如何线性求出每个i点到其他点的最长距离。
显然,根节点的S=max[root]。我们用G[i]表示从i的父亲结点及以上转移过来的最长长度。显然,对于结点i,S=max(max[i],G[i])然后再考虑G[i]的转移。如果i是父亲max[fa]经过的路径,G[i]=max(maxx[fa],G[i]),否则G[I]=max(max[fa],G[i])。随便YY即可。
                                                                        

②我们不妨设每棵树求出来的S的最大值是D[i],最小值是E[i]。显然,我们应该摆成以上形式,使得E[i]最大的在上面。我们再设底下的的E[I]是降序排序的,分别标为E[1],E[2],E[3]。。。那么ans=max(max1+max2+L,max2+max3+2*L)。

③细节很坑。我们要把ans与每一个D[i]求max。别忘了再特判只有1个或2个的情况。

【代码】

#include<cstdio>#include<algorithm>#define N 500005using namespace std;struct arr{int go,next,s;}a[N*2];int f[N],Max[N],Maxx[N],temp[N],D[N],E[N],end[N],G[N];int num,p,i,x,y,z,n,m,L,ans,cnt,Cnt,max1,max2,max3;void add(int u,int v,int w){  a[++cnt].go=v;a[cnt].next=end[u];a[cnt].s=w;end[u]=cnt;}int get(int u){return (f[u]==u)?u:f[u]=get(f[u]);}void Union(int u,int v){  int uu=get(u),vv=get(v);  if (uu==vv) return;f[vv]=uu;}void Get(int k,int fa){  for (int i=end[k];i;i=a[i].next)  {    int go=a[i].go;if (go==fa) continue;    Get(go,k);    if (Max[go]+a[i].s>Max[k]) Max[k]=Max[go]+a[i].s,temp[k]=go;  }  for (int i=end[k];i;i=a[i].next)  {    int go=a[i].go;    if (go!=fa&&Max[go]+a[i].s>Maxx[k]&&temp[k]!=go) Maxx[k]=Max[go]+a[i].s;  }}void Dfs(int k,int fa){  for (int i=end[k];i;i=a[i].next)  {    int go=a[i].go;if (go==fa) continue;    G[go]=max(G[k],(temp[k]==go)?Maxx[k]:Max[k])+a[i].s;    int now=max(Max[go],G[go]);    if (now>D[p]) D[p]=now;    if (now<E[p]) E[p]=now;    Dfs(go,k);  }}int main(){  scanf("%d%d%d",&n,&m,&L);  for (i=1;i<=n;i++) f[i]=i;  for (i=1;i<=m;i++)    scanf("%d%d%d",&x,&y,&z),add(++x,++y,z),add(y,x,z),Union(x,y);  for (p=1;p<=n;p++)    if (f[p]==p)    {      Get(p,0);G[p]=0;D[p]=E[p]=Max[p];      Dfs(p,0);ans=max(ans,D[p]);      if (E[p]>max1) {max3=max2;max2=max1;max1=E[p];}      else if (E[p]>max2) {max3=max2;max2=E[p];}      else if (E[p]>max3) max3=E[p];      Cnt++;    }  if (Cnt>1) ans=max(ans,max1+max2+L);  if (Cnt>2) ans=max(ans,max2+max3+2*L);  printf("%d",ans);  return 0;}

0 1