并查集

复制代码
6-1 并查集 ///find函数可能有点难理解,自己尝试画下图随便理解好吧 ///M是合并,Q是询问是否在一个树中 #include<bits/stdc++.h> using namespace std; const int N=10010; int n,m; int q[N]; int find(int x) { if(q[x]!=x) q[x]=find(q[x]));///如果x不是树的头结点,向上循环查找到头结点为止. ///可以自己画图理解一下; return q[x]; } int main() { cin>>n>>m; for(int i=1;i<=n;i++) q[i]=i; while(m--) { char op[2]; int a,b; scanf("%s%d%d",&op,&a,&b); if(op[0]=='M') q[find(a)]=find(b); else { if(find(a)==find(b)) cout<<"YES"<<endl; else cout<<"NO"<<endl; } } return 0; } 6-2 查并集,但是可能输出长度 #include<bits/stdc++.h> using namespace std; const int N=100010; int n,m; int p[N],size[N]; int find(int x) { if(p[x]!=x) p[x]=find(p[x]); return p[x]; } int main() { cin>>n>>m; for(int i=1;i<=n;i++) { p[i]=i; size[i]=1; } while(m--) { ///C为合并,Q1为 检查是否在同一区间,Q2检查长度; char op[2]; scanf("%s",&op); int a,b; if(op[0]=='C') { cin>>a>>b; if(find(a)==find(b)) continue;///特判 size[find(b)]+=size[find(a)]; p[find(a)]=find(b); } else if(op[1]=='1') { cin>>a>>b; if(find(a)==find(b)) cout<<"YES"<<endl; else cout<<"No"<<endl; } else if(op[1]=='2') { cin>>a; cout<<size[find(a)]<<endl; } } } 6-3 食物链 https://www.luogu.com.cn/problem/P2024 ///这题是真难,看视频加理解一共花了俩小时 #include<bits/stdc++.h> using namespace std; const int N=100010; int n,m,res=0; int p[N],d[N]; int find(int x) { if(p[x]!=x)///与以往不同,这里的d数组刚开始的作用是,x节点与父节点的距离,经过find函数递归之后 ///d数组变成了,x节点到头节点的距离,这样就可以来判断是不是同一类和接下来做题了 { int t=find(p[x]); d[x]+=d[p[x]];///因为要用到p[x],所以先储存; p[x]=t; } return p[x]; } int main() { cin>>n>>m; for(int i=1;i<=n;i++) p[i]=i; while(m--) { int a,b,c; cin>>c>>a>>b; if(a>n||b>n) res++; else { if(c==1) { int pa=find (a),pb=find(b); if(pa==pb&&(d[a]-d[b])%3) res++;///3个一循环;如果A和B是同类,而且他俩已经在合并过了,那么两者的距离相减,如果余三不得0那么就是错的; else { p[pa]=pb; d[pa]=d[b]-d[a]; } } else { int pa=find(a),pb=find(b); if(pa==pb&&(d[a]-d[b]-1)%3) res++; else if(pa!=pb) { p[pa]=pb; d[pa]=d[b]+1-d[a]; } } } } cout<<res; return 0; } 6-4 自动程序检测装置 https://www.luogu.com.cn/problem/P1955 ///这个题是我得知有查并集这个算法的起初 ///查并集+离散化,题目i和j都开到10的九次方了,不用离散化等着爆吧 ///本题思路 #include<bits/stdc++.h> using namespace std; const int N=400010; int t,n; int a[2*N],p[N],d[2*N],b[N],c[N]; int find(int x)/// { if(p[x]!=x) p[x]=find(p[x]); return p[x]; } int main() { cin>>t; while(t--) { cin>>n; for(int i=1;i<=n;i++) { cin>>a[2*i-1]>>a[2*i]>>c[i]; d[2*i-1]=a[2*i-1],d[2*i]=a[2*i]; } sort(d+1,d+2*n+1); int h=0; b[0]=-1; for(int i=1;i<=2*n;i++) if(b[h]!=d[i]) b[++h]=d[i];///离散化完成,用了两次这个离散化模板了,感觉yxc老师的那个离散化模板用不了一点 for(int i=1;i<=h;i++) p[i]=i;///更新爹节点 for(int i=1;i<=n;i++) { if(c[i]==0) continue; int x=lower_bound(b+1,b+h+1,a[2*i-1])-b; int y=lower_bound(b+1,b+h+1,a[2*i])-b; if(find(x)==find(y)) continue; else p[find(x)]=find(y);///将所有相等的元素,放进树里面;接下来的就是狠狠的查找有没有内鬼 } bool f=false; for(int i=1;i<=n;i++) { if(c[i]==1) continue; int x=lower_bound(b+1,b+h+1,a[2*i-1])-b; int y=lower_bound(b+1,b+h+1,a[2*i])-b; if(find(x)==find(y))///查找内鬼,如果有人在相等的树里面,就是内鬼! { cout<<"NO"<<endl; f=true; break; } } if(!f) cout<<"YES"<<endl; } return 0; } 6-6 A-bugs http://bailian.openjudge.cn/practice/2492/ ///此题考察并查集的应用,考察的是加权并查集,类似于食物链,需要考虑两者之间存在距离关系 ///大题思路:每当输出两个数字是,代表这两个数字之间肯定是异性关系(如果两者满足距离为奇数) ///每当新的数加入到另一个树上时,如果符合条件,那么就意味这两者的距离之差至少为1,并且都是奇数,再向下拓展一位,也就是距离为偶数的,就是同性 ///可以用da-db%2来判断,如果两者在一个树上并且满足这个条件,这两个人就是同性! #include<bits/stdc++.h> using namespace std; const int N=10010; int n,m,t; int p[N],d[N]; int find(int x) { if(x!=p[x]) { int t=find(p[x]); d[x]+=d[p[x]]; p[x]=t; } return p[x]; } int main() { cin>>t; int res=1; while(t--) { cin>>n>>m; for(int i=1;i<=n;i++) p[i]=i; memset(d,0,sizeof d); bool f=false; while(m--) { int a,b; cin>>a>>b; int pa=find(a),pb=find(b); if(pa==pb&&(d[a]-d[b])%2==0) f=true; else { p[pa]=p[b]; d[pa]=d[b]+1-d[a]; } } if(f) { printf("Scenario #%d:\n",res++); cout<<"Suspicious bugs found!"<<endl; } else { printf("Scenario #%d:\n",res++); cout<<"No suspicious bugs found!"<<endl; } cout<<endl; } return 0; }
复制代码

 

复制代码
//https://www.luogu.com.cn/problem/P1525 #include<bits/stdc++.h> using namespace std; const int N=2e5+10; int n,m,res,p[N],vis[N]; struct node { int a,b,c; bool operator<(const node&w) const { return c>w.c; } }per[N]; int find(int x) { if(x!=p[x]) p[x]=find(p[x]); return p[x]; } int main() { cin>>n>>m; //根据题意理解,考虑贪心,根据库鲁斯卡尔,即最小生成树的思路 //如果将边权从大到小排序,然后开始进行合并 //考虑如何合并,首先如果两人已经在同一个监狱了,由于我们从大到小排序,所以这个一定是最大的,直接输出结束即可 //如果没在,那么要进行一个判断,设两人为x和y,有 x不能和y在同一个监狱,z不能和x在同一个监狱,那么z会和y在一个监狱 //所以如果此时x没有标记z的话,y就变成z,然后等待另一个y,将y和z合并,即 敌人的敌人是朋友 //没有合并的不需要考虑了,因为我们一定是最大的 for(int i=1;i<=n;i++) p[i]=i; for(int i=1;i<=m;i++) cin>>per[i].a>>per[i].b>>per[i].c; sort(per+1,per+1+m); for(int i=1;i<=m;i++){ int x=per[i].a,y=per[i].b,w=per[i].c; if(find(x)==find(y)) return cout<<w,0; if(vis[x]==0) vis[x]=y; //如果此时x没有先前的敌对,那就先标记 else p[find(y)]=find(vis[x]); //将y和x的敌对放到同一个监狱 if(vis[y]==0) vis[y]=x; else p[find(x)]=find(vis[y]); } cout<<0; //如果都符合,输出0 return 0; }
复制代码
复制代码
//https://www.luogu.com.cn/problem/P1621 #include<bits/stdc++.h> using namespace std; const int N=2e5+10; int n,m,res,ans,num,k; int prime[N],prime_[N],p[N]; bool vis[N]; int find(int x) { if(x!=p[x]) p[x]=find(p[x]); return p[x]; } int main() { cin>>n>>m>>k; for(int i=n;i<=m;i++) p[i]=i; //预处理出来2-m的所有质数 for(int i=2;i<=m;i++){ if(!vis[i]) prime_[++res]=i; for(int j=1;prime_[j]<=m/i;j++){ vis[prime_[j]*i]=true; if(!(i%prime_[j])) break; } } //预处理k-m的质数 for(int i=1;i<=res;i++) if(prime_[i]>=k) prime[++num]=prime_[i]; //对于每个质数,算出对应的这个数的最小倍数,然后遍历这个质数对应最小的质数到最大数的区间即可 for(int i=1;i<=num;i++){ int x=(n+prime[i]-1)/prime[i]*prime[i]; for(int j=x;j<=m;j+=prime[i]) p[find(j)]=find(x); } //统计集合数量,即根节点还是本身的数量 for(int i=n;i<=m;i++) if(p[i]==i) ans++; cout<<ans; return 0; }
复制代码

 带权并查集:

复制代码
//https://www.luogu.com.cn/problem/P1196 #include <bits/stdc++.h> #define int long long using namespace std; const int N=1e5+10,mod=1e9+7; signed main() { std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); int n; cin>>n; vector<int>p(N+1),sz(N+1),dep(N+1); for(int i=1;i<=N;i++) p[i]=i,sz[i]=1; function<int(int)>find; find=[&](int x)->int{ if(x==p[x]) return p[x]; int root=find(p[x]); dep[x]+=dep[p[x]]; return p[x]=root; }; while(n--){ char op; int a,b; cin>>op>>a>>b; if(op=='M'){ int u=find(a),v=find(b); if(u==v) continue; p[u]=v,dep[u]=sz[v],sz[v]+=sz[u]; } else{ if(find(a)!=find(b)) cout<<-1<<'\n'; else cout<<abs(dep[a]-dep[b])-1<<'\n'; } } return 0; }
复制代码

 


__EOF__

本文作者Sakurajimamai
本文链接https://www.cnblogs.com/o-Sakurajimamai-o/p/17427994.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   o-Sakurajimamai-o  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
-- --
点击右上角即可分享
微信分享提示