【并查集离散化合根】連結 / Connectivity AtCoder - 2159

Think:
1知识点:【并查集离散化合根】
2题意:第1行输入n, k, l,表示图中有n个城市、k对由公路连接的城市、l对由铁路连接的城市,第2行至第k+1行每行输入由公路连接的两个城市(无重复路线),第k+2行至第k+l+1行每行输入由铁路连接的两个城市(无重复路线),题目要求输出对于每个城市与其相连的城市中有多少个城市是既可以通过公路到达也可以通过铁路到达这个城市。
2.1数据范围:
·2 <= n <= 2e5
·1 <= k <= 1e5
·1 <= k <= 1e5
3解题流程:
3.1知道题意之后,我们开始思考判断两个点是否既可以通过公路到达也可以通过铁路到达,我们或许脑海中首先出现的想法是最短路,进而我们会思考最短路是否可行,继续深入思考我们可以发现,首先最短路我们通常求两个点之间的最短距离,而本题目则是只需要判断两个城市是否通过公路可达和是否通过铁路可达,因此最短路想法首先出现第一点不适应表现,第二,我们发现题目中城市的数量是1e5级别且需要对于每个城市与其既通过公路相连也通过铁路相连的城市,而我们通常使用的最短路算法中,Dijkstra算法是1对1,时间复杂度为O((N+M)*lgN),而spfa算法是1对n,时间复杂度最坏情况为O(NM),而Floyd算法是n对n,时间复杂度为O(N^3),而我们需要得到n对n的信息,因此从时间复杂度分析,Dijkstra算法、spfa算法、Floyd算法均不适应,因此我们暂时放弃最短路思路
3.2思考题目所求,我们从求解条件开始思考应该如何解决,首先我们需要知道城市之间公路连接信息和城市之间铁路连接信息,因此我们思考是否可将道路信息转化为两个图,转化为两个图之后,我们发现,我们无法通过邻接矩阵存储图中边的信息,因此我们思考转而思考我们建图的目的是得到与一个城市相连的城市中可以通过公路相连的城市和可以通过铁路相连的城市,我们进而思考,我们是否可以通过别的数据结构和算法迅速知道两个城市是否有公路可达和两个城市是否由铁路可达,因此我们会逐渐想到并查集,通过并查集我们可以迅速判断两个点是否在一个集合内,也就是说,若我们分别对公路图和分别对铁路图进行并查集,那么我们可以很快判断两个城市是否可以通过公路达到和是否可以通过铁路达到,因此我们开始思考我们已经可以迅速判断两个城市是否由公路可达和是否由铁路可达,那么我们要以O(n^2)的时间复杂度判断对于每个城市与其既可以通过公路可达也可以通过铁路可达的城市数量吗?回答是否定的,由于城市数量级别为2e5,很明显我们不能通过直接遍历。
3.3通过3.2的思路可知,我们已经想到如何迅速判断两个城市是否通过公路可达和是否通过铁路可达,那么我们如何能够以更短的时间得到与一个城市相连的其它符合条件的城市数量呢?假设如果我们只有一个并查集,那么我们是不是可以知道对于某一个连通子图节点的数量也就是总集合元素中根相同的点的数量,进而记录每个连通子图的节点数量,查询时便可以通过根迅速锁定所在连通子图,进而迅速得到所在连通子图节点的数量,因此我们开始思考是否可以将两个并查集集合合并为一个并查集集合?答案是肯定的,我们可以通过离散化将同一个节点在两个并查集集合中根的值离散化为唯一的根,也就是说,如果两个节点在两个并查集集合内的根均是相同的,那么这两个点的新根同样相同,进而并查集离散化合根之后,我们记录对于不同的根分别包含多少数量的节点。

vjudge题目链接

以下为Accepted代码

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

LL a[204014], b[204014], c[204014], d[204014];
int book[204014];

LL get_f(LL f[], int x);

int main(){
  int n, k, l, i, u, v, t1, t2, id;
  while(~scanf("%d %d %d", &n, &k, &l)){
    for(i = 1; i <= n; i++){
      a[i] = b[i] = i;
    }
    for(i = 0; i < k; i++){
      scanf("%d %d", &u, &v);
      t1 = get_f(a, u);
      t2 = get_f(a, v);
      if(t1 != t2) a[t2] = t1;
    }

    for(i = 0; i < l; i++){
      scanf("%d %d", &u, &v);
      t1 = get_f(b, u);
      t2 = get_f(b, v);
      if(t1 != t2) b[t2] = t1;
    }

    LL e = n+1;
    for(i = 1; i <= n; i++){
      ///c[i] = a[i] + e*b[i];
      c[i] = get_f(a, i) + e*get_f(b, i);
      d[i] = c[i];
    }

    sort(d+1, d+1+n);
    memset(book, 0, sizeof(book));
    for(i = 1; i <= n; i++){
      id = lower_bound(d+1, d+1+n, c[i]) - d;
      book[id]++;
    }
    for(i = 1; i <= n; i++){
      id = lower_bound(d+1, d+1+n, c[i]) - d;
      printf("%d%c", book[id], i == n? '\n': ' ');
    }
  }
  return 0;
}
LL get_f(LL f[], int x){
  if(x == f[x]) return f[x];
  return f[x] = get_f(f, f[x]);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值