题目链接
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);
}
京公网安备 11010502036488号