目录:
前置知识
题目描述
样例与数据范围
解题思路
AC 代码
前置知识:
并查集 (路径压缩),快速排序(sort),结构体
题目描述:

样例与数据范围:

解题思路:
在并查集的专题里,当然是用并查集啦!
刚开始的时候,小编还以为是用搜索的 qwq (你看这个题,他多像搜索啊),再读一读题目,n 个点,m 条边,这很明显是个图论题啊(废话,并查集不就是图论的一部分吗),再想一想,利用并查集维护图的连通性,建立一个 sum 数组维护集合大小,就可以完美解决了。
为了使用时方便,可以先将原数据按照人气值 d 从小到大排个序。
不难发现(是真的不难),排序后在前面的询问能经过的边在后面的边一定能经过。所以我们可以按顺序连边(所以,要将 q 组询问的数据按照 x 从小到大的顺序排个序),对于每一个询问加入能够加入的边,利用并查集维护图的连通性,用 sum 数组维护集合大小。对于加入的一条边 ,若 a , b 在同一集合中,则可以直接忽略;否则对答案的贡献为 sum[a] * sum[b]。
因为是双向通道,二并查集的统计方式之后统计一次(也就是说(a , b) 和 (b , a) 两种法案,只会统计一次,所以不要忘记将答案乘2。
因为将询问数据排序后可能会让输出的顺序变乱,所以要建立一个 ans 数组来存储每次询问的答案
AC 代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
#include<vector>
#include<string>
#include<cstring>
#include<queue>//头文件不解释
using namespace std;
int test,Q,n,m,tot;//test是数据的组数,Q是每组数据中的询问次数,n是景点数,m是边数
int fa[100001];//记录其祖先元素
int sum[100001];//用于维护集合的大小
int ans[100001];//储存每次询问的答案
struct no//记录原始数据
{
int x;//出发景点
int y;//到达景点
int d;//人气值
}a[100001];
struct de//记录询问的数据
{
int x;//记录要求不能超过的人气值的大小
int num;//记录第几次询问
}q[100001];
bool cmp1(no a,no b)//将原始数据按从小到大的顺序排个序
{
return a.d<b.d;
}
bool cmp2(de a,de b)//将询问数据也按从小到大的顺序排个序
{
return a.x<b.x;
}
int find(int x)//并查集基本操作,寻找该节点的祖先
{
if(!fa[x])
{
return x;
}
return fa[x]=find(fa[x]);
}
void add(int x,int y)//并查集基本操作,合并两个集合,不过要穿差一点东西,以到达维护sum数组的目的
{
int u=find(x);//寻找x祖先元素
int v=find(y);//寻找y祖先元素
if(u==v)//如果两个元素已在同一个集合,则直接忽略
{
return;
}
tot+=sum[u]*sum[v]*2;//否则,这两个点对答案的贡献就是sum[u]*sum[v]*2(不要忘记乘2)
fa[u]=v;//合并两个集合
sum[v]+=sum[u];//维护sum数组
sum[u]=0;//这里写不写都可以(只要你对并查集理解得够深,就能明白)
}
int main()
{
scanf("%d",&test);//输入test
while(test--)//test次循环
{
scanf("%d%d%d",&n,&m,&Q);//读入
for(int i=1;i<=m;i++)//读入
{
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].d);
}
sort(a+1,a+m+1,cmp1);//给原始数据排序
for(int i=1;i<=Q;i++)//读入
{
scanf("%d",&q[i].x);//读入
q[i].num=i;//记录一下是第几次询问
}
sort(q+1,q+1+Q,cmp2);//给询问数据排序
tot=0;//记录景点的对数
for(int i=1;i<=n;i++)//并查集基本操作之一————初始化
{
fa[i]=0;
sum[i]=1;//初始时每个元素都是一个集合,所以sum数组的值都为1
}
int j=1;//记录当前询问到第几组道路
for(int i=1;i<=Q;i++)
{
while((j<=m)&&(a[j].d<=q[i].x))
{
add(a[j].x,a[j].y);//合并两个集合
j++;//询问数加1
}
ans[q[i].num]=tot;//记录答案
}
for(int i=1;i<=Q;i++)//输出
{
printf("%d\n",ans[i]);
}
}
return 0;//完结撒花
}


这篇博客介绍了如何利用并查集解决一道图论问题,通过对数据进行排序和路径压缩,维护集合大小来求解。同时,文章还强调了在处理过程中需要考虑双向通道的情况,并使用一个ans数组来确保正确输出顺序。
&spm=1001.2101.3001.5002&articleId=119683836&d=1&t=3&u=7f79cc4e0a724f6a8be73aa81f3fe6bc)
347

被折叠的 条评论
为什么被折叠?



