Codeforces Round #582 (Div. 3) G. Path Queries
个人感觉这道题目的思路比较像最小瓶颈生成树,首先我们要理解什么是最小瓶颈生成树,最小瓶颈生成树是在一张图上生成一棵树要求最大的边最小,可以感性理解一下,最小生成树一定是最小瓶颈生成树,但最小瓶颈生成树不一定是最小生成树,所以我们可以用最小生成树来做这道题目。
回忆(kruskal)最小生成树的过程,当前加进去的边一定是使这些联通块中的点两两联通的最大边的最小值。如果只有一个联通块就直接输出(n*(n-1)/2)就可以了,(其中(n)是当前联通的点的个数),如果有多个联通块每个把它们相加就可以了。但是如果这次操作联通的是两个联通块,这样就会重算,需要判断一下。为了方便我们直接计算每次连上这条边对点对数量的贡献就是(size[a]*size[b])(由乘法原理易得)。所以我们只需要在原来的点对数上加上就可以了。
#include<algorithm>
#include<iostream>
#include<vector>
#include<cstdio>
#define ll long long
using namespace std;
const int N=2e5+100;
struct edge{
int s,e,v;
};
vector<edge>ed;
int n,m,maxq;
int f[N],size[N],q[N];
ll ans[N];
inline bool cmp(edge a,edge b) {return a.v<b.v;}
inline int getf(int x) {return f[x]==x ? x:f[x]=getf(f[x]);}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n-1;i++)
{
int s,e,v;
scanf("%d%d%d",&s,&e,&v);
ed.push_back((edge){s,e,v});
}
for (int i=1;i<=m;i++)
{
scanf("%d",q+i);
maxq=max(maxq,q[i]);
}
for (int i=1;i<=n;i++)
{
size[i]=1;
f[i]=i;
}
sort(ed.begin(),ed.end(),cmp);
int be=0,nv=0;
for (int i=0;i<(int)ed.size();i++)
{
int a=getf(ed[i].s),b=getf(ed[i].e);
nv=ed[i].v;
ll now=0;
if (a!=b)
{
now=(ll)size[a]*size[b];
size[b]+=size[a];
size[a]=0;
f[a]=b;
}
ans[nv]+=now;
}
for (int i=1;i<=maxq;i++)
ans[i]+=ans[i-1];
for (int i=1;i<=m;i++)
cout<<ans[q[i]]<<" ";
return 0;
}