题解:牛课 小美的朋友关系

题目

小美认为,在人际交往中,但是随着时间的流逝,朋友的关系也是会慢慢变淡的,最终朋友关系就淡忘了。
现在初始有一些朋友关系,存在一些事件会导致两个人淡忘了他们的朋友关系。小美想知道某一时刻中,某两人是否可以通过朋友介绍互相认识?
事件共有 2 种:
1. u v:代表编号 u 的人和编号 v 的人淡忘了他们的朋友关系。
2. u v:代表小美查询编号 u 的人和编号 v 的人是否能通过朋友介绍互相认识。

输入

第一行输入三个正整数 n, m, q,代表总人数,初始的朋友关系数量,发生的事件数量。
接下来 m 行,每行输入两个正整数 u, v,代表初始编号 u 的人和编号 v 的人是朋友关系。
接下来 q 行,每行输入三个正整数 op, u, v,含义如题目描述所述。

1\leq n \leq 10^9\\ 1\leq m,q\leq 10^5\\ 1\leq u, v\leq n\\ 1\leq op\leq 2
保证至少存在一次查询操作

示例

输入:

5 3 5
1 2
2 3
4 5
1 1 5
2 1 3
2 1 4
1 1 2
2 1 3

输出:

Yes
No
No

代码

import sys
from collections import defaultdict

class DisjointSet:
    def __init__(self) -> None:
        # 因为 n 可能很大,用 list 太大了
        self.p = {}

    def find(self, x):
        if x not in self.p:
            return x
        if self.p[x] != x:
            self.p[x] = self.find(self.p[x])
        return self.p[x]

    def union(self, x, y):
        px = self.find(x)
        py = self.find(y)
        if px != py:
            self.p[py] = px

if __name__ == '__main__':
    N, M, Q = map(int, sys.stdin.readline().strip().split())
    relats = defaultdict(set)
    for _ in range(M):
        u, v = map(int, sys.stdin.readline().strip().split())
        relats[u].add(v)
    events = []
    for _ in range(Q):
        op, u, v = map(int, sys.stdin.readline().strip().split())
        if op == 1:
            if v in relats[u]:
                relats[u].remove(v)
                events.append((op, u, v))
            elif u in relats[v]:
                relats[v].remove(u)
                events.append((op, u, v))
        else:
            events.append((op, u, v))
    ds = DisjointSet()
    # 超时的主要原因,n 太大,没必要为了空间复杂度增加时间复杂度
    # for i in range(1, N+1):
    #     for j in range(i+1, N+1):
    #         if j in relats[i]:
    #             # ds.p[i] = i
    #             # ds.p[j] = i
    #             ds.union(i, j)
    for k, v in relats.items():
        for j in v:
            ds.union(k, j)
    print_str = ''
    while(events):
        op, u, v = events.pop()
        if op == 1:
            ds.union(u, v)
        else:
            if ds.find(u) == ds.find(v):
                print_str = 'Yes\n' + print_str
            else:
                print_str = 'No\n' + print_str
    sys.stdout.write(print_str)

时间复杂度:O(max(m, n) * n)
该方法中没有使用 rank 压缩路径,使用后并查集合并的时间复杂度为 O(log^*n),则总时间复杂度变为 O(max(m, n) * log^*n)

关于“小美朋友关系图谱”或其社交网络分析,以下是整合的信息与解答: --- 可以通过构建一个动态的朋友关系模型来理解小美的社交网络变化。这种模型通常涉及以下几个方面: 1. **初始化朋友关系** 初始状态可以表示为一组节点(代表个体)和边(代表朋友关系)。例如,可以用邻接矩阵或者邻接表存储这些信息。假设初始有 N 个人,则每个人都可以被编号为从 1 到 N。 2. **处理事件导致的变化** 随着时间推移,某些事件会改变朋友之间的关系。比如,“A 和 B 成为了朋友”,这可以在图中增加一条连接 A 和 B 的边;而“A 和 B 变得疏远了”,则需要删除这条边。 3. **计算连通性** 对于任意两人 X 和 Y,判断他们是否仍然属于同一个朋友圈子的问题,可以通过并查集(Union-Find 数据结构)高效解决。每次新增或断开关系时更新集合即可。 4. **可视化展示** 如果希望直观地看到整个社交网络的状态变迁过程,可以选择图形化工具绘制网络拓扑图。Python 库 NetworkX 是一种常用选择,它支持创建复杂网络并且提供多种布局算法以优化显示效果。 5. **数据分析角度** 分析该社交网络可以从多个维度展开:平均路径长度反映群体紧密程度;聚类系数衡量局部聚集效应等特性指标均有助于揭示隐藏模式。 --- ### 示例代码实现 下面是一个简单的 Python 实现用于模拟上述逻辑: ```python class FriendNetwork: def __init__(self, n): self.parent = list(range(n)) # 查找根节点同时做路径压缩 def find(self, x): if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) return self.parent[x] # 合并两个集合 def union(self, x, y): root_x = self.find(x) root_y = self.find(y) if root_x != root_y: self.parent[root_x] = root_y # 删除某条边的操作相当于重新划分所属组别 def remove_edge(self, x, y): all_members = set() for i in range(len(self.parent)): all_members.add(self.find(i)) new_parents = {} count = 0 for member in sorted(all_members): new_parents[member] = count count += 1 updated_parent = [] for p in self.parent: updated_parent.append(new_parents[self.find(p)]) self.parent = updated_parent # 测试用例 network = FriendNetwork(5) # 初始化五个用户 network.union(0, 1) # 用户0和用户1成为好友 network.union(1, 2) # 用户1和用户2也成为好友 print(network.find(0) == network.find(2)) # True 表明他们在同一圈子里 network.remove_edge(1, 2) # 断绝用户1和用户2的关系 print(network.find(0) == network.find(2)) # False 因为圈子分裂了 ``` --- ### 注意事项 - 并查集适用于快速查询两点间的联通情况但不直接保存具体哪些点相连需结合其他数据结构补充完整功能。 - 当规模较大时应考虑性能优化措施如按秩合并及路径压缩技巧提升效率。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值