这个题做法比较多 我的做法比较锻(傻)炼(比)代(超)码(级)能(麻)力(烦)。。
对于每个点维护mx cmx f g分别表示最长链 次长链和最长链的方案数 次长链的方案数
先dfs一遍把以每个点为根的子树的这些值都维护出来 然后考虑换根之后
这些值该如何维护 漫长的分类讨论..(看代码)
感觉代码或许有bug 但是这题数据太水了 过了样例1A了。。如果发现哪里不对欢迎指正!
标解的做法呢就是根据直径的性质 发现满足答案的边一定是一条不分叉的连续路径。。显然只要产生分叉就说明这一段可以被替代。。然后随便写写了~
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=200011;
int head[N],next[N*2],key[N*2],w[N*2],tot;
long long mx[N],cmx[N],dist[N],vis[N],f[N],g[N];
long long d,ans,gs;
int out[N];
void add(int x,int y,int z)
{
tot++;
next[tot]=head[x];
head[x]=tot;
key[tot]=y;
w[tot]=z;
}
void dfs(int x,int fa)//求出直径
{
for(int i=head[x];i;i=next[i])
{
int y=key[i];
if(y!=fa)
{
dfs(y,x);
if(mx[x]<=mx[y]+w[i])cmx[x]=mx[x],mx[x]=mx[y]+w[i];
else if(cmx[x]<mx[y]+w[i])cmx[x]=mx[y]+w[i];
}
}
for(int i=head[x];i;i=next[i])
{
int y=key[i];
if(y!=fa&&mx[x]==mx[y]+w[i])f[x]+=f[y];
if(y!=fa&&cmx[x]==mx[y]+w[i])g[x]+=f[y];
}
d=max(cmx[x]+mx[x],d);
}
void slove(int x)
{
vis[x]=1;
for(int i=head[x];i;i=next[i])
{
int y=key[i];
if(vis[y]==0)
{
if(mx[y]+w[i]==mx[x])//最长链在这颗子树
if(mx[y]+w[i]+cmx[x]==d&&g[x]*f[y]==gs)ans++;
//最长链不在这棵子树
if(mx[y]+w[i]+mx[x]==d&&f[y]*f[x]==gs)ans++;
}
}
for(int i=head[x];i;i=next[i])
{
int y=key[i];
if(vis[y]==0)
{
if(mx[y]+w[i]==mx[x])//最长链在这棵子树 此时次长连一定不在这棵子树
{
if(mx[y]<cmx[x]+w[i])
{
mx[y]=cmx[x]+w[i];
f[y]=g[x];
}
else if(mx[y]==cmx[x]+w[i])f[y]+=g[x];
else if(mx[y]>cmx[x]+w[i])
{
if(cmx[y]<cmx[x]+w[i])
{
cmx[y]=cmx[x]+w[i];
g[y]=g[x];
}
else if(cmx[y]==cmx[x]+w[i])g[y]+=g[x];
}
}
else
{
cmx[y]=mx[y];g[y]=f[y];
mx[y]=mx[x]+w[i];f[y]=f[x];
}
slove(y);
}
}
}
int main()
{
int n;cin>>n;
for(int i=1;i<n;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
out[x]++;
out[y]++;
}
for(int i=1;i<=n;i++)
if(out[i]==1)f[i]=1;
dfs(1,-1);
for(int i=1;i<=n;i++)
if(mx[i]==cmx[i]&&mx[i])g[i]--;
for(int i=1;i<=n;i++)if(mx[i]+cmx[i]==d)gs+=f[i]*g[i];
slove(1);
printf("%lld\n%lld\n",d,ans);
}