题目链接
https://ac.nowcoder.com/acm/contest/7780/C
解题思路
并查集。
大致思路:先把连接着两个大于k的点的边加入,再判断连接着小于等于k的两个点的边,两点是否同根,若同根则答案++,并且不将边加入;若不同根,则答案不变,并且加入边。
两个点,其中存在至少一个点小于等于k,若二者存在边,且同根,说明两点已经相连,若再直接连接二者将形成环,所以ans++。
代码步骤:
第一次循环,初始化每个点的根为自己;
第二次循环,先把两个点都大于k且未相连的边加入;
第三次循环,判断两个点中存在一个点小于等于k的边连接的两个点的根是否相同,若同,ans++;不同,两点相连,加入边。
为什么要先把大于k的边加入,再循环小于等于k的?
举个例子:
假设纯黑点为小于等于k的点,空心点为大于k的点。
如果一次循环插入所有边:
若先插入边1,没问题可以插入;再插入边2,依旧可以;此时,要插入边3,但是边3连接的两个点是大于k的点,又因为同根,所以不会ans不会自加,显然错误。
AC代码
#include<bits/stdc++.h> #define sc(x) scanf("%d",&x) #define pr(x) printf("%d",x); using namespace std; const int N=2e6+100; int u[N],v[N],fa[N],n,m,k,ans; int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); } void join(int x,int y){ int rx=find(x),ry=find(y); if(rx!=ry) fa[rx]=ry; } int main(){ sc(n);sc(m);sc(k); for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=m;i++){ sc(u[i]);sc(v[i]); if(u[i]>k&&v[i]>k&&find(u[i])!=find(v[i])) join(u[i],v[i]); } for(int i=1;i<=m;i++){ if(u[i]<=k||v[i]<=k) { int ru=find(u[i]),rv=find(v[i]); if(ru==rv) ++ans; else join(ru,rv); } } pr(ans); }