bzoj3124[Sdoi2013]直径(树dp)

来源:互联网 发布:泰语字母发音软件 编辑:程序博客网 时间:2024/05/17 04:46

Description

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

Input

第一行包含一个整数N,表示节点数。
接下来N-1行,每行三个整数a, b, c ,表示点 a和点b之间有一条长度为c
的无向边。

Output

共两行。第一行一个整数,表示直径的长度。第二行一个整数,表示被所有
直径经过的边的数量。

Sample Input

6
3 1 1000
1 4 10
4 2 100
4 5 50
4 6 100

Sample Output

1110
2

第一问很简单,两边dfs即可。
对于第二问,我们需要注意到以下几点
(设直径较浅的端点为up,较深的端点为low,dp[x]xup的路径长度)
1.答案一定在已经确定出的直径上
2.当存在一段由uv的最长路径dis,使得dis==dp[x](dis==dp[low]dp[x])时,x以上(以下)的路径全部变为不合法.
所以我们可以先求出一条直径,同时dp出不与直径相交的最长路径长度,然后统计出合法区间中的点数即可.
代码

#include <cstdio>#include <cstring>#define maxn 200005#define maxx 400005#define mem(a,b) memset(a,b,sizeof(a))inline void read(int& x){   char c=getchar();x=0;int y=1;    while(c<'0'||c>'9'){if(c=='-') y=-1;c=getchar();}    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();    x*=y;}typedef long long ll;ll dp[maxn],f[maxn],ans;int n,num,hea[maxn],lef[maxn],rig[maxn],cnt,range,target,retarget,upside,downside;bool used;struct road{int en,w,nex;}ro[maxx];inline void add(int x,int y,int z){ro[num].en=y;ro[num].nex=hea[x];ro[num].w=z;hea[x]=num++;}inline void dfs(int x,int y=0,ll z=0){   if(z>ans) ans=z,target=x;    for(int i=hea[x];i!=-1;i=ro[i].nex)    {   int v=ro[i].en;        if(v==y) continue;        dfs(v,x,z+ro[i].w);    }}inline void redfs(int x,int y=0,ll z=0){   lef[x]=rig[x]=++cnt;dp[x]=z;    if(ans<z) ans=z,retarget=x,range=lef[x];    for(int i=hea[x];i!=-1;i=ro[i].nex)    {   int v=ro[i].en;        if(v==y) continue;        redfs(v,x,z+ro[i].w);        if(f[x]<f[v]+ro[i].w) f[x]=f[v]+ro[i].w;    }    rig[x]=cnt;}inline void dfs3(int x,int y=0){   ll fir=0;    for(int i=hea[x];i!=-1;i=ro[i].nex)    {   int v=ro[i].en;if(v==y) continue;        if(lef[v]<=range&&rig[v]>=range) dfs3(v,x);        else if(fir<f[v]+ro[i].w) fir=f[v]+ro[i].w;    }    if(!used&&fir==dp[x]) upside=x,used=1;    if(fir==(dp[retarget]-dp[x])) downside=x;}inline void dfs4(int x,int y=0){   if(dp[x]>=dp[upside]&&dp[x]<=dp[downside]) ++ans;    for(int i=hea[x];i!=-1;i=ro[i].nex)    {   int v=ro[i].en;if(v==y) continue;        if(lef[v]<=range&&rig[v]>=range) dfs4(v,x);    }}int main(){   read(n);mem(hea,-1);int x=0,y=0,z=0;    for(int i=1;i<n;++i)    {   read(x);read(y);read(z);        add(x,y,z);add(y,x,z);    }    dfs(1);ans=0;redfs(target);printf("%lld\n",ans);    dfs3(target);ans=0;dfs4(target);    printf("%lld",ans-1);    return 0;}