链接
题意:
给出你一棵树,n个点,(n-1)条边,让求
∑
i
=
1
n
∑
j
=
i
+
1
n
f
(
i
,
j
)
\sum_{i=1}^n\sum_{j=i+1}^{n} f(i,j)
∑i=1n∑j=i+1nf(i,j).
f
(
i
,
j
)
f(i,j)
f(i,j)表示从i到j最短路径上最大边权值。
分析:
首先我们分析最短路径上的最大边权值,那么只要经过最大边权的两个点,肯定贡献就是这个边权了,那么我们就可以这样想 :
那么会有多少个点对代价是这条边那,就是这条边左边连的点数乘以右边连的点数,及3*3乘以点对,之后那,之后这个边就没了,然后看其中剩下他们最大的边。这样分析?但是每次都需要维护出最大边两端连的点数,而且每个点连的点数在变化,直接爆搜一定会超时。
那我们换个思路,既然删边不行,我们加边总可以把。这样我们就能用带权并查集维护出一条边上两段点数了。然后我看上面删边是选最大边,那么加边就是选最小边。
ll n;
struct node {
ll u,v,w;
}q[maxn];
bool cmp(node a,node b){
return a.w<b.w;
}
ll f[maxn],sz[maxn];
ll Find(ll x){
if(f[x]==x) return x;
return f[x] = Find(f[x]);
}
void merge(ll x,ll y){
ll fx,fy;
fx=Find(x),fy=Find(y);
if(fx==fy) return ;
if(sz[fx]>sz[fy]) swap(fx,fy);
f[fx]=fy;
sz[fy]+=sz[fx];
}
void solve()
{
cin>>n;
for(int i=1;i<=n;i++) f[i]=i,sz[i]=1;
for(int i=1;i<n;i++){
scanf("%lld%lld%lld",&q[i].u,&q[i].v,&q[i].w);
}
sort(q+1,q+n,cmp);
ll ans=0;
for(int i=1;i<n;i++){
ans+=q[i].w*sz[Find(q[i].u)]*sz[Find(q[i].v)];
merge(q[i].u,q[i].v);
}
cout<<ans<<endl;
}