Codeforcesラウンド#582(DIV。3)-G。パスQueries-互いに素セット
【問題の内容】
あなたの木を与え、どのように多くの単純なパスを尋ねる\((U、V)\) 、満たす\(uは\)にする(V \)\超えないのこのパスに最大値を\(k個\) 。\(Q \)のクエリ。
【溶液】
互いに素セット
すべてのサイド・バイ・重量小から大、クエリ値に\(K_I \)は、小から大にもあります。各クエリの値のための\(K_I \) 、右側以下\(K_I \)辺と一緒に組み合わせることによって確認しました。ユニコムブロックは組み合わせのために、各ブロックユニコム応答の寄与である(\ \ {FRACサイズ\タイムズ(-size。1)} {2} \) 、ユニコムブロックを直接加算することができるすべての貢献。だから、毎回2つのノード、2つのノードのマイナス、プラス組み合わせ貢献が可能な第1の貢献ユニコムブロックをマージします。
【コード】
/*
* @Author: Simon
* @Date: 2019-09-07 10:00:30
* @Last Modified by: Simon
* @Last Modified time: 2019-09-07 10:29:11
*/
#include<bits/stdc++.h>
using namespace std;
typedef int Int;
#define int long long
#define INF 0x3f3f3f3f
#define maxn 200005
struct node{
int u,v,w;
node(){}
node(int u,int v,int w):u(u),v(v),w(w){}
bool operator <(const node&a)const{
return w<a.w;
}
}g[maxn],q[maxn];
int Set[maxn],Rank[maxn];
void init(int n){ //初始化
for(int i=0;i<=n;i++){
Set[i]=i;Rank[i]=1;
}
}
int cal(int x){ //计算联通块的贡献
return x*(x-1)/2;
}
int Find(int u){ //并查集的查找根节点
return Set[u]==u?u:Set[u]=Find(Set[u]);
}
int res=0;
int ans[maxn];
void merge(int u,int v){ //并查集的合并
u=Find(u),v=Find(v);
if(Rank[u]<Rank[v]) swap(u,v);
res-=cal(Rank[u]),res-=cal(Rank[v]); //减去u,v所在联通块的贡献
Set[v]=u;Rank[u]+=Rank[v]; //合并两个联通块
res+=cal(Rank[u]); //再加上合并后的联通块的贡献
}
Int main(){
#ifndef ONLINE_JUDGE
//freopen("input.in","r",stdin);
//freopen("output.out","w",stdout);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
int n,m;cin>>n>>m;init(n);
for(int i=1;i<n;i++){
cin>>g[i].u>>g[i].v>>g[i].w;
}
sort(g+1,g+n);
for(int i=1;i<=m;i++){
cin>>q[i].w;q[i].u=i;
}
sort(q+1,q+m+1);
int pos=1;
for(int i=1;i<=m;i++){
while(pos<n&&g[pos].w<=q[i].w){
merge(g[pos].u,g[pos].v);
pos++;
}
ans[q[i].u]=res;
}
for(int i=1;i<=m;i++) cout<<ans[i]<<" \n"[i==m];
#ifndef ONLINE_JUDGE
cout<<endl;system("pause");
#endif
return 0;
}