JZOJ 4223. 【五校联考3day1】旅游【并查集】


题意:

m m m条边,每个边都有一个权值 w i w_i wi
q q q次询问,对于每个询问也有一个权值 q i q_i qi
辉夜在第 i i i次询问时只能通过 w i < = q i w_i<=q_i wi<=qi的边
对每次询问有多少个点对可以满足让辉夜从 a a a出发走到 b b b


分析:

很眼熟的题好吧
但就是不知道为什么没有做题记录和博客
回归正题,我们可以对所有边和询问的权值升序排序,面临一个新的询问时可以控制一个指针加入满足条件的边,然后答案则可以用并查集来判断是否在同一连通块,若不在那该边会产生的贡献就是两个连通块大小的乘积
时间复杂度为 O ( q + m ) O(q+m) O(q+m)


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<queue>
#define LL long long
using namespace std;
inline LL read()
{
    
    
	LL s=0,f=1; char c=getchar();
	while(c<'0'||c>'9') {
    
    if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') {
    
    s=s*10+c-'0';c=getchar();}
	return s*f;
}
struct node{
    
    
	LL x,y,d;
}e[100005];
struct rnm{
    
    
	LL w,k;	
}x[5005];
bool cmp(node a,node b) {
    
    return a.d<b.d;}
bool cmpp(rnm a,rnm b) {
    
    return a.w<b.w;}
LL f[20005],size[20005];
LL find(LL i) {
    
    return f[i]==i?i:f[i]=find(f[i]);}
LL ans[5005];
int main()
{
    
    
	LL t=read();
	while(t--)
	{
    
    
		LL n=read(),m=read(),q=read();
		for(LL i=1;i<=n;i++) f[i]=i,size[i]=1;
		for(LL i=1;i<=m;i++) e[i].x=read(),e[i].y=read(),e[i].d=read();
		sort(e+1,e+1+m,cmp);
		for(LL i=1;i<=q;i++) x[i].w=read(),x[i].k=i;
		sort(x+1,x+1+q,cmpp);
		LL k=1;
		memset(ans,0,sizeof(ans));
		for(LL i=1;i<=q;i++)
		{
    
    
			ans[x[i].k]=ans[x[i-1].k];
			while(k<=m&&e[k].d<=x[i].w) 
			{
    
    
				LL a=find(e[k].y),b=find(e[k].x);
				if(a==b) {
    
    k++;continue;}
				f[a]=b;
				ans[x[i].k]+=size[a]*size[b];
				size[b]+=size[a];
				k++;
			}
		}	
		for(LL i=1;i<=q;i++) printf("%lld\n",ans[i]*2);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35786326/article/details/109082008