很好的一道treedp,当然也挺烦的
首先不难想到先求出一个直径,然后穷举每条边,看他删除之后直径大小是否会变,变了的话就是必须经过的边
下面我们就要想怎么优化,本人语言表达略差,还是配合程序说吧。

  1 type node=record
  2        point,next:longint;
  3        cost:int64;
  4      end;
  5 
  6 var f:array[0..200010] of int64;
  7     pr,fr:array[0..200010,0..2] of longint;
  8     g,h:array[0..200010,0..2] of int64;
  9     p:array[0..200010] of longint;
 10     edge:array[0..400010] of node;
 11     v:array[0..200010] of boolean;
 12     i,n,len,ans,x,y,z:longint;
 13 
 14 procedure add(x,y,z:longint);
 15   begin
 16     inc(len);
 17     edge[len].point:=y;
 18     edge[len].cost:=z;
 19     edge[len].next:=p[x];
 20     p[x]:=len;
 21   end;
 22 
 23 function max(a,b:int64):int64;
 24   begin
 25     if a>b then exit(a) else exit(b);
 26   end;
 27 
 28 procedure dp(x:longint);
 29   var i,y:longint;
 30   begin
 31     i:=p[x];
 32     v[x]:=true;
 33     while i<>-1 do
 34     begin
 35       y:=edge[i].point;
 36       if not v[y] then
 37       begin
 38         dp(y);
 39         f[x]:=max(f[x],f[y]);  //f[]表示以x为根的子树中最长的路径长
 40         if g[y,0]+edge[i].cost>g[x,0] then  //维护以x为根走到底最长的3条路径(显然不能有两条路径经过同一个孩子)
 41 //这里要维护3条,因为要考虑删边的时候碰巧干掉了其中一条,如果单纯求树的直径的时候只要维护最长和次长即可)
 42         begin
 43           g[x,2]:=g[x,1];
 44           fr[x,2]:=fr[x,1];
 45           g[x,1]:=g[x,0];
 46           fr[x,1]:=fr[x,0];
 47           g[x,0]:=g[y,0]+edge[i].cost;
 48           fr[x,0]:=y;  //从哪转移来的
 49         end
 50         else if (g[y,0]+edge[i].cost>g[x,1]) then
 51         begin
 52           g[x,2]:=g[x,1];
 53           fr[x,2]:=fr[x,1];
 54           g[x,1]:=g[y,0]+edge[i].cost;
 55           fr[x,1]:=y;
 56         end
 57         else if (g[y,0]+edge[i].cost>g[x,2]) then
 58         begin
 59           g[x,2]:=g[y,0]+edge[i].cost;
 60           fr[x,2]:=y;
 61         end;
 62         if f[y]>h[x,0] then  //h维护的是以x为根的子树中最长的2条路径(不经过根)
 63         begin
 64           h[x,1]:=h[x,0];
 65           pr[x,1]:=pr[x,0];
 66           h[x,0]:=f[y];
 67           pr[x,0]:=y;  //从哪转移来的
 68         end
 69         else if f[y]>h[x,1] then
 70         begin
 71           h[x,1]:=f[y];
 72           pr[x,1]:=y;
 73         end;
 74       end;
 75       i:=edge[i].next;
 76     end;
 77     f[x]:=max(f[x],g[x,1]+g[x,0]);
 78   end;
 79 
 80 procedure dfs(x:longint;l,d:int64); //l表示以x为终点且不是起点x的后辈的最长路径,d表示之前搜索到的不经过x的最长路径
 81   var i,y:longint;
 82       l1,l2,l3:int64;
 83   begin
 84     i:=p[x];
 85     v[x]:=true;
 86     while i<>-1 do
 87     begin
 88       y:=edge[i].point;
 89       if not v[y] then   //大批的分类讨论
 90       begin
 91         l1:=f[y];
 92         if y=pr[x,0] then l2:=h[x,1]
 93         else l2:=h[x,0];
 94         l2:=max(l2,d);
 95 
 96         if y=fr[x,0] then l2:=max(l2,g[x,1]+g[x,2])
 97         else if y=fr[x,1] then l2:=max(l2,g[x,0]+g[x,2])
 98         else l2:=max(l2,g[x,0]+g[x,1]);
 99 
100         if y=fr[x,0] then l3:=g[x,1]
101         else l3:=g[x,0];
102         l2:=max(l2,l+l3);
103         if max(l1,l2)<f[1] then inc(ans);
104         dfs(y,max(l3,l)+edge[i].cost,l2);
105       end;
106       i:=edge[i].next;
107     end;
108   end;
109 
110 begin
111   readln(n);
112   fillchar(p,sizeof(p),255);
113   for i:=1 to n-1 do
114   begin
115     readln(x,y,z);
116     add(x,y,z);
117     add(y,x,z);
118   end;
119   dp(1);
120   writeln(f[1]);
121   fillchar(v,sizeof(v),false);
122   ans:=0;
123   dfs(1,0,0);
124   writeln(ans);
125 end.
View Code

 

posted on 2014-09-11 21:45  acphile  阅读(177)  评论(0编辑  收藏  举报