首页 > 代码库 > bzoj 3246 [Ioi2013] Dreaming 题解

bzoj 3246 [Ioi2013] Dreaming 题解

【原题】

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 500005
using 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;
}