题目链接
思路:感觉生成树的题目练得不够多,完全想不到这一块去。。。
我们直接统计对数不好求,对于边权小于i的点对数我们其实可以先求一下1到i-1的点对说求前缀和,那么怎么从前往后统计呢,我们可以利用最小生成树,以为我们新加的边对于已加的边来说肯定是最大了的,点对数可以通过记录一条边的两个端点所在的连通块大小相乘就是答案。
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+1;
typedef long long ll;
ll ans[maxn];
int n,m,t,father[maxn],size[maxn];
struct cxk{
int u,v,w;
};
vector<cxk>g;
int findfather(int x)
{
if(x==father[x]) return x;
int i=findfather(father[x]);
father[x]=i;
return i;
}
bool cmp(const cxk &a,const cxk &b)
{
return a.w<b.w;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<n;++i){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
g.push_back({u,v,w});
}
sort(g.begin(),g.end(),cmp);
for(int i=1;i<=n;++i) father[i]=i,size[i]=1;
for(auto i:g)
{
int fa=findfather(i.u),fb=findfather(i.v);
ll sum=0;
sum=(ll)size[fa]*size[fb];
size[fb]+=size[fa];
size[fa]=0;
father[fa]=fb;
ans[i.w]+=sum;
}
for(int i=1;i<maxn;++i) ans[i]+=ans[i-1];
for(int i=1;i<=m;++i)
{
scanf("%d",&t);
printf("%lld ",ans[t]);
}
}