BZOJ 3124 [Sdoi2013] 直径(树形DP)

 

题目大意

 

小 Q 最近学习了一些图论知识。根据课本,有如下定义。树:无回路且连通的无向图,每条边都有正整数的权值来表示其长度。如果一棵树有 N 个节点,可以证明其有且仅有 N-1 条边。路径:一棵树上,任意两个节点之间最多有一条简单路径。我们用 dis(a, b) 表示点 a 和点 b 的路径上各边长度之和。称 dis(a,b) 为 a、b 两个节点间的距离。 

直径:一棵树上,最长的路径为树的直径。树的直径可能不是唯一的。

现在小 Q 想知道,对于给定的一棵树,其直径的长度是多少,以及有多少条边满足所有的直径都经过该边。

 

对于100%的测试数据:2≤N≤200000,所有点的编号都在 1..N 的范围内,边的权值≤10^9。

 

做法分析

 

可以这样想,先求出树的直径是多大,然后枚举边,如果这条边被所有的直径经过,那么,删掉这条边之后,剩下的两棵树的直径必然小于没删掉之前树的直径

现在的问题转化为了怎么求树的直径,以及如何快速的得到删掉一条边后,两棵子树的直径的最大者

可以用树形DP搞定这些东西

 

维护一个数组 f[i][j] 表示从 i 开始,走到它的子树的用叶子节点中,第 j 长的长度是多少,fk[i][j] 表示第 j 长的路径经过的是哪个儿子得到的

维护一个数组 g[i][j] 表示以 i 为根的子树的直径是多大

维护一个数组 h[i][j] 表示以 i 的儿子节点为根的子树中,树的直径第 j 大的是多少,he[i][j] 表示第 j 大的直径是哪个儿子得到的

 

那么,先一遍 DFS 可以求出这些值

再用一遍 DFS 便可以利用这些值得到删掉一条边之后,两棵子树的直径中最大的是多少了

 

参考代码

 

BZOJ 3124 [Sdoi2013] 直径(树形DP)
  1 #include <iostream>

  2 #include <cstring>

  3 #include <cstdio>

  4 

  5 using namespace std;

  6 

  7 typedef long long LL;

  8 

  9 const int N=200005;

 10 

 11 struct ARC {

 12     int u, next;

 13     LL val;

 14     inline void init(int U, LL VAL, int NEXT) {

 15         u=U, val=VAL, next=NEXT;

 16     }

 17 } arc[N<<1];

 18 int head[N], n, fk[N][3], he[N][2];

 19 LL g[N], f[N][3], h[N][2];

 20 

 21 void DP(int u, int pre) {

 22     f[u][0]=f[u][1]=f[u][2]=g[u]=h[u][0]=h[u][1]=0LL;

 23 

 24     for(int e=head[u]; e!=-1; e=arc[e].next) {

 25         int v=arc[e].u;

 26         LL val=arc[e].val;

 27         if(v==pre) continue;

 28 

 29         DP(v, u);

 30 

 31         if(f[v][0]+val>f[u][0]) {

 32             f[u][2]=f[u][1], fk[u][2]=fk[u][1];

 33             f[u][1]=f[u][0], fk[u][1]=fk[u][0];

 34             f[u][0]=f[v][0]+val, fk[u][0]=v;

 35         }

 36         else if(f[v][0]+val>f[u][1]) {

 37             f[u][2]=f[u][1], fk[u][2]=fk[u][1];

 38             f[u][1]=f[v][0]+val, fk[u][1]=v;

 39         }

 40         else if(f[v][0]+val>f[u][2]) f[u][2]=f[v][0]+val, fk[u][2]=v;

 41 

 42         if(g[v]>g[u]) g[u]=g[v];

 43 

 44         if(g[v]>h[u][0]) {

 45             h[u][1]=h[u][0], he[u][1]=he[u][0];

 46             h[u][0]=g[v], he[u][0]=v;

 47         }

 48         else if(g[v]>h[u][1]) h[u][1]=g[v], he[u][1]=v;

 49     }

 50     g[u]=max(g[u], f[u][0]+f[u][1]);

 51 }

 52 

 53 LL dia;

 54 int ans;

 55 

 56 void DFS(int u, int pre, LL len, LL Max) {

 57     for(int e=head[u]; e!=-1; e=arc[e].next) {

 58         int v=arc[e].u;

 59         LL len1=g[v], len2, len3;

 60         if(v==pre) continue;

 61 

 62         if(v==he[u][0]) len2=h[u][1];

 63         else len2=h[u][0];

 64 

 65         len2=max(len2, Max);

 66 

 67         if(v==fk[u][0]) len2=max(len2, f[u][1]+f[u][2]);

 68         else if(v==fk[u][1]) len2=max(len2, f[u][0]+f[u][2]);

 69         else len2=max(len2, f[u][0]+f[u][1]);

 70 

 71         if(v==fk[u][0]) len3=f[u][1];

 72         else len3=f[u][0];

 73 

 74         len2=max(len2, len+len3);

 75 

 76         if(max(len1, len2)<dia) ans++;

 77 

 78         DFS(v, u, max(len3, len)+arc[e].val, len2);

 79     }

 80 }

 81 

 82 int main() {

 83 //    freopen("in", "r", stdin);

 84     for(; scanf("%d", &n)!=EOF; ) {

 85         fill(head, head+1+n, -1);

 86         for(int i=1, a, b; i<n; i++) {

 87             LL c;

 88             scanf("%d%d%lld", &a, &b, &c);

 89             arc[i].init(b, c, head[a]);

 90             head[a]=i;

 91             arc[i+n].init(a, c, head[b]);

 92             head[b]=i+n;

 93         }

 94         DP(1, -1);

 95         dia=g[1], ans=0;

 96         DFS(1, -1, 0, 0);

 97         printf("%lld\n%d\n", dia, ans);

 98     }

 99     return 0;

100 }
BZOJ 3124

 

题目链接 & AC 通道

 

BZOJ 3124 [Sdoi2013] 直径

 

 

 

你可能感兴趣的:(ZOJ)