地址:http://codeforces.com/contest/1213/problem/G
思路:并查集+离线查询
对于m个查询,先将其由小到大排序,这样就能保证后面的答案一定包含前面的答案。
再将所有边按照权值wi由小到大排序,对于第i小的查询ti,将wi<ti的边合并,其边所贡献的答案为 边两边所在集合的乘积 d[a]*d[b]
Code:
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
typedef pair<int,int> pr;
const int MAX_N=2e5+5;
struct node{
int u,v,w;
bool operator<(const node &A){
return w<A.w;
}
};
int n,m;
int id[MAX_N];
LL d[MAX_N],ans;
node a[MAX_N];
pr que[MAX_N];
LL res[MAX_N];
int Find(int x){
if(id[x]!=x) id[x]=Find(id[x]);
return id[x];
}
void Union(int a,int b){
int fa=Find(a);
int fb=Find(b);
ans+=d[fa]*d[fb];
id[fa]=fb;
d[fb]+=d[fa];
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=0;i<=n;++i)
id[i]=i,d[i]=1;
int u,v,w;
for(int i=1;i<n;++i)
{
cin>>u>>v>>w;
a[i]={u,v,w};
}
sort(a+1,a+n);
for(int i=0;i<m;++i)
{
cin>>w;
que[i]={w,i};
}
sort(que,que+m);
int t,li=1,l=0,r=0;
int fa,fb;
while(r<m){
t=que[l].first;
while(li<n&&a[li].w<=t){
Union(a[li].u,a[li].v);
++li;
}
while(l<m&&que[l].first==que[r].first){
res[que[l].second]=ans;
++l;
}
++r;
}
for(int i=0;i<m-1;++i)
cout<<res[i]<<" ";
cout<<res[m-1]<<endl;
return 0;
}