JZOJ4588. 【NOI2016模拟7.7】冷战 + 按秩合并讲解

题目大意

给定一副 N 个点的图。动态的往图中加边。现在有两种操作,每种操作读入三个数Ord,u,v
Ord=0 : 在 u,v 间连一条双向边
Ord=1 : 询问 u,v 最早在什么时候联通。

N,M <= 105

解题思路

对于维护联通块的问题,我们可以用并查集来维护,实际上并查集就是维护了一颗树,如果我们用按秩合并的话树的深度就是 O(LogN) 的。我们对于并查集上的边,加上一个边权,设为加入的时间,那么每次询问,就是询问 u,v 之间最大的边权,由于深度是 O(LogN) ,那么查询暴力跳也就是 O(LogN) 的了。

按秩合并

按秩合并是实际上就是对两个集合合并时的一种策略,对于一个集合,我们可以把他们抽象成一颗树,我们这棵树的最大深度是 Deep ,那么当我们合并两个这两个集合时我们分情况讨论。假设现在要合并集合 i 和集合j Deepi>=Deepj
1. Deepi>Deepj :我们把 j 集合合并到i集合, Deepi 不变。
2. Deepi=Deepj :我们把 j 集合合并到i集合, Deepi=Deepi+1
我们发现,只有在两个集合深度相同是新集合的深度才会加1,也就是说最坏情况就是类似二叉树的形态,所以说树的高度最大也只是 O(LogN) 的。

程序

//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

const int MAXN = 5e5 + 5;

int N, M, Fa[MAXN], Num[MAXN], fa[MAXN], Time[MAXN], L[MAXN], R[MAXN];

int Get(int Now) {
    if (Fa[Now] == Now) return Now;
    Fa[Now] = Get(Fa[Now]);
    return Fa[Now];
}

void Link(int x, int y, int time) {
    int f1 = Get(x), f2 = Get(y);
    if (f1 == f2) return;
    if (Num[f1] > Num[f2]) swap(f1, f2);
    Fa[f1] = f2, fa[f1] = f2, Time[f1] = time;
    Num[f2] = max(Num[f2], Num[f1] + 1);
}

int Query(int x, int y) {
    int f1 = Get(x), f2 = Get(y);
    if (f1 != f2 || x == y) return 0;
    L[0] = R[0] = 0;
    for (; fa[x] != x; x = fa[x]) L[++ L[0]] = x; L[++ L[0]] = x;
    for (; fa[y] != y; y = fa[y]) R[++ R[0]] = y; R[++ R[0]] = y;
    for (; L[L[0]] == R[R[0]] && L[0] && R[0]; L[0] --, R[0] --);
    int Ans = 0;
    if (L[0]) Ans = max(Ans, Time[L[L[0]]]);
    if (R[0]) Ans = max(Ans, Time[R[R[0]]]);
    return Ans;
}

int main() {
    scanf("%d%d", &N, &M);
    int LastAns = 0, Cnt = 0;
    for (int i = 1; i <= N; i ++) Fa[i] = i;
    for (int i = 1; i <= M; i ++) {
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        y ^= LastAns, z ^= LastAns;
        if (x == 0) Link(y, z, ++ Cnt); else {
            LastAns = Query(y, z);
            printf("%d\n", LastAns);
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值