#并查集#JZOJ 4223 旅游

题目

多次询问有多少个无序点对\((x,y)\)
满足至少有一条最大边权\(\leq d\)的路径


分析

离线询问,用并查集加边,每次产生的贡献为\(2*siz[x]*siz[y]\)


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
const int N=100011; typedef long long lll;
struct rec{int x,rk;}q[N]; struct node{int x,y,w;}e[N];
int n,m,Q,son[N],f[N]; lll ans[N],now;
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void print(lll ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
bool cmp1(node x,node y){return x.w<y.w;}
bool cmp2(rec x,rec y){return x.x<y.x;}
inline signed getf(int u){return f[u]==u?u:f[u]=getf(f[u]);}
signed main(){
	for (rr int Test=iut();Test;--Test){
		n=iut(),m=iut(),Q=iut();
		for (rr int i=1;i<=m;++i)
		    e[i]=(node){iut(),iut(),iut()};
		for (rr int i=1;i<=n;++i) son[i]=1,f[i]=i;
		sort(e+1,e+1+m,cmp1);
		for (rr int i=1;i<=Q;++i) q[i]=(rec){iut(),i};
		sort(q+1,q+1+Q,cmp2);
		for (rr int i=1,j=1;i<=Q;++i){
			for (;j<=m&&e[j].w<=q[i].x;++j){
				rr int fa=getf(e[j].x),fb=getf(e[j].y);
				if (fa>fb) fa^=fb,fb^=fa,fa^=fb;
				if (fa!=fb){
					now+=2ll*son[fa]*son[fb];
					f[fa]=fb,son[fb]+=son[fa];
				}
			}
			ans[q[i].rk]=now;
		}
		for (rr int i=1;i<=Q;++i) print(ans[i]),putchar(10);
	}
	return 0;
} 
posted @ 2020-08-15 17:04  lemondinosaur  阅读(58)  评论(0编辑  收藏  举报