1 条题解

  • 0
    @ 2026-9-26 1:41:24

    思路:

    首先,本蒟蒻雀食很蒻。

    首先,因为我们只需要保证编号 ≤k\le k 的点不在简单环上(简单环:又称简单回路,图的顶点序列中,除了第一个顶点和最后一个顶点相同外,其余顶点不重复出现的回路叫简单回路。或者说,若通路或回路不重复地包含相同的边,则它是简单的)。

    也就是两个编号 >k>k 的点之间的连边是没有必要删除的,因为假如有一个编号 ≤k\le k 的点在一个简单环上,我们肯定可以通过删除一条与这个点相连的边使得这个环消失,所以我们直接删除一条与这个编号 ≤k\le k 的点相连的边使得它脱离这个环显然是不劣的。因此我们直接保留所有 >k>k 的点相互之间的连边即可。

    然后将这些边所连的点通过并查集归在一起,否则后续难度将会飙升。

    然后枚举每一条边,如果这条边所连的两个点有一个的编号 >k>k,直接跳过即可。

    如果这条边所连的两个点有一个的编号 ≤k\le k,那么我们就考虑保留这条边是否会产生环。如果保留这条边会产生环,那我们必然舍弃,计入答案即可;那如果保留这条边不会产生环,我们也是需要保留的,因为我们肯定是要能连尽量连,这样才能保证删除的边最少。

    其实此时问题可以转化为 kk 个独立点和若干个连通块,每个独立点和每个连通块之间最多只能连一条边。

    当上述情况发生时,其实就相当于一个独立点和一个连通块第一次尝试连边,但是因为我们无法保证后续是否会出现新的边来把这两部分连起来,为保证最优性,我们肯定是要保留这条边的。

    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;
    }
    

    AC记录

    本文借鉴于 @Dream_poetry 大佬的题解,本人在此对一些晦涩难懂的概念做解释。

    后记:

    5.15.1 修改后删除一些不必要的元素。

    • 1

    [POI 2012] TOU-Tour de Byteotia比赛路线

    信息

    ID
    3865
    时间
    5500ms
    内存
    128MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者