1 条题解
-
0
思路:
首先,本蒟蒻雀食很蒻。首先,因为我们只需要保证编号 的点不在简单环上(简单环:又称简单回路,图的顶点序列中,除了第一个顶点和最后一个顶点相同外,其余顶点不重复出现的回路叫简单回路。或者说,若通路或回路不重复地包含相同的边,则它是简单的)。
也就是两个编号 的点之间的连边是没有必要删除的,因为假如有一个编号 的点在一个简单环上,我们肯定可以通过删除一条与这个点相连的边使得这个环消失,所以我们直接删除一条与这个编号 的点相连的边使得它脱离这个环显然是不劣的。因此我们直接保留所有 的点相互之间的连边即可。
然后将这些边所连的点通过并查集归在一起,否则后续难度将会飙升。
然后枚举每一条边,如果这条边所连的两个点有一个的编号 ,直接跳过即可。
如果这条边所连的两个点有一个的编号 ,那么我们就考虑保留这条边是否会产生环。如果保留这条边会产生环,那我们必然舍弃,计入答案即可;那如果保留这条边不会产生环,我们也是需要保留的,因为我们肯定是要能连尽量连,这样才能保证删除的边最少。
其实此时问题可以转化为 个独立点和若干个连通块,每个独立点和每个连通块之间最多只能连一条边。
当上述情况发生时,其实就相当于一个独立点和一个连通块第一次尝试连边,但是因为我们无法保证后续是否会出现新的边来把这两部分连起来,为保证最优性,我们肯定是要保留这条边的。
Code:
#include<bits/stdc++.h> using namespace std; #define int long long #define N 2000005 int n,m,k,fa[N],cnt; struct node { int x,y; }e[N],ans[N]; int find(int x) {//并查集找根 if (fa[x]!=x) { fa[x]=find(fa[x]); } return fa[x]; } signed main() { ios::sync_with_stdio(0); cin.tie(0); //快读模板 cin>>n>>m; for (int i=1; i<=n; i++)fa[i]=i;//把每一个点设为自己的根,方便并集 cin>>k; for (int i=1; i<=m; i++) { cin>>e[i].x>>e[i].y; if (e[i].x>k && e[i].y>k) fa[find(e[i].x)]=find(e[i].y); } for (int i=1; i<=m; i++) { int u=find(e[i].x); int v=find(e[i].y); if (e[i].x<=k || e[i].y<=k) { if (u==v) { cnt++; ans[cnt]=e[i]; } else fa[u]=v; } } cout<<cnt<<"\n"; for (int i=1; i<=cnt; i++) { if (ans[i].x<ans[i].y) { cout<<ans[i].x<<' '<<ans[i].y<<"\n"; } else { cout<<ans[i].y<<' '<<ans[i].x<<"\n"; } } return 0; }本文借鉴于 @Dream_poetry 大佬的题解,本人在此对一些
晦涩难懂的概念做解释。后记:
修改后删除一些不必要的元素。
- 1
信息
- ID
- 3865
- 时间
- 5500ms
- 内存
- 128MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者