【并查集】【模拟】Codeforces 698B & 699D Fix a Tree

本文解析了 Codeforces 平台上的两道题目,详细介绍了如何使用并查集算法来解决树结构的问题,包括错误树的修正和环的检测。通过实际代码展示了算法的具体实现过程。

题目链接:

  http://codeforces.com/problemset/problem/698/B

  http://codeforces.com/problemset/problem/699/D

题目大意:

  通过给定当前节点的父亲给你一棵有错的树,可能有多个根和环,输出改成正确的一棵树至少要修改几个节点的父亲和修改后所有点的父亲值

题目思路:

  【并查集】【模拟】

  用并查集把成环的归在一起(类似强连通分量),然后统计分量数并修改。

  第一个出现的当作根,其余的每一块连通分量都去掉一条边改为连接到根上。

 

 1 //
 2 //by coolxxx
 3 ////<bits/stdc++.h>
 4 #include<iostream>
 5 #include<algorithm>
 6 #include<string>
 7 #include<iomanip>
 8 #include<map>
 9 #include<memory.h>
10 #include<time.h>
11 #include<stdio.h>
12 #include<stdlib.h>
13 #include<string.h>
14 //#include<stdbool.h>
15 #include<math.h>
16 #define min(a,b) ((a)<(b)?(a):(b))
17 #define max(a,b) ((a)>(b)?(a):(b))
18 #define abs(a) ((a)>0?(a):(-(a)))
19 #define lowbit(a) (a&(-a))
20 #define sqr(a) ((a)*(a))
21 #define swap(a,b) ((a)^=(b),(b)^=(a),(a)^=(b))
22 #define mem(a,b) memset(a,b,sizeof(a))
23 #define eps (1e-8)
24 #define J 10
25 #define MAX 0x7f7f7f7f
26 #define PI 3.14159265358979323
27 #define N 200004
28 using namespace std;
29 typedef long long LL;
30 int cas,cass;
31 int n,m,lll,ans;
32 int root;
33 int a[N],fa[N];
34 int zhao(int aa)
35 {
36     if(fa[aa]==0 || fa[aa]==aa)return aa;
37     return (fa[aa]=zhao(fa[aa]));
38 }
39 int main()
40 {
41     #ifndef ONLINE_JUDGE
42     freopen("1.txt","r",stdin);
43 //    freopen("2.txt","w",stdout);
44     #endif
45     int i,j,fx,fy;
46 //    for(scanf("%d",&cas);cas;cas--)
47 //    for(scanf("%d",&cas),cass=1;cass<=cas;cass++)
48     while(~scanf("%d",&n))
49 //    while(~scanf("%d",&n))
50     {
51         mem(fa,0);ans=0;root=0;
52         for(i=1;i<=n;i++)
53         {
54             scanf("%d",&a[i]);
55             if(a[i]==i)root=i;
56             fx=zhao(a[i]);
57             fy=zhao(i);
58             if(fx!=fy)
59                 fa[fy]=fx;
60         }
61         for(i=1;i<=n;i++)
62         {
63             fa[i]=zhao(i);
64             if(fa[i]==i)
65             {
66                 if(!root)
67                 {
68                     ans++;
69                     root=i;
70                     a[i]=i;
71                 }
72                 else if(i!=root)
73                     ans++,a[i]=root;
74             }
75         }
76         printf("%d\n",ans);
77         for(i=1;i<=n;i++)
78             printf("%d ",a[i]);
79         puts("");
80     }
81     return 0;
82 }
83 /*
84 //
85 
86 //
87 */
View Code

 

转载于:https://www.cnblogs.com/Coolxxx/p/5786051.html

### 关于 CodeForces 892E 的解题思路分析 #### 使用可撤销并查集解决最小生成树中的边集合验证问题 针对给定的无向图以及多个询问,每个询问涉及一组特定的边,并要求判断这组边能否同时存在于某棵最小生成树中。此问题可以通过结合Kruskal算法构建最小生成树的过程来求解,在这一过程中利用到的是按照权重升序排列后的边逐步加入至森林结构之中[^1]。 为了高效处理多次查询而不影响后续操作的结果,引入了带有回溯功能的数据结构&mdash;&mdash;即所谓的&ldquo;可撤销并查集&rdquo;。这种特殊形式的并查集允许执行合并(union)的同时记录下每一次变动以便之后能够恢复原状;当完成一次查询判定后即可通过一系列反向动作使数据结构回到初始状态,从而不影响其他独立事件的发生逻辑[^3]。 具体实现方法如下: - 将所有的边依据其权重从小到大排序; - 对每一个询问所涉及到的边也做同样的预处理; - 开始遍历已排序好的全局边列表,每当遇到属于当前待检验询问范围内的边时,则尝试将其纳入现有连通分量内; - 如果发现形成环路则说明该询问无法满足条件; - 同样地,任何不属于当前询问但同样处于相同权值下的其它边也应该被考虑进来以确保最终形成的MST是最优解的一部分; - 完成一轮测试后记得清除所有临时更改使得系统重置为未受干扰的状态准备迎接下一个挑战。 ```cpp #include &lt;bits/stdc++.h&gt; using namespace std; struct Edge { int u, v; }; class DSUWithRollback { public: vector&lt;int&gt; parent, rank, historyParent, historyRank; void init(int n){ parent.resize(n); iota(parent.begin(), parent.end(), 0); // Fill with identity mapping. rank.assign(n, 0); historyParent.clear(); historyRank.clear(); } int findSet(int i) {return (parent[i]==i)?i:(findSet(parent[i]));} bool isSameSet(int i, int j){ return findSet(i)==findSet(j);} void unionSets(int i, int j){ if (!isSameSet(i,j)){ historyParent.push_back(findSet(i)); historyParent.push_back(findSet(j)); historyRank.push_back(rank[findSet(i)]); historyRank.push_back(rank[findSet(j)]); int x=findSet(i), y=findSet(j); if (rank[x]&gt;rank[y]) swap(x,y); parent[x]=y; if (rank[x]==rank[y]) ++rank[y]; } } void rollback(){ while(!historyParent.empty()){ parent[historyParent.back()]=historyParent.back(); historyParent.pop_back(); rank[historyParent.back()] = historyRank.back(); historyParent.pop_back(); historyRank.pop_back(); } } }; ``` 上述代码展示了如何创建一个支持撤销机制的并查集类`DSUWithRollback`,它可以在不破坏原有连接关系的前提下安全地进行节点间的联合与查找操作。此外还提供了用于追踪变化历史的方法,方便在必要时候撤消最近的一系列更动。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值