Tour de Byteotia 题解

赛时很唐。

可以将删掉改成保留边会好些一点。

靠考虑保留什么点,首先若两个点都大于 \(k\) 肯定是需要保留的,这时再判断有小于等于 \(k\) 的点,如果这两个点尚未联通这条边肯定是要保留的,若这一条联通肯定这一条是不能保留的。

考虑为什么是对的,首先保留的边肯定构成的是若干个联通块,我们需要通过一些边来联通他们,使得这些点不会有环,显然就是不会连向一个联通块两次,这样转化就很简单了,能连的都连上,其余的都放在贡献里,最后输出方案。

时间复杂度 \(O(m\alpha(n))\)

#include<bits/stdc++.h>
const int N=2e6+5;
using namespace std;
int n, m, k, ans;
int u[N], v[N],fa[N];
int find(int x) {
	return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void merge(int u, int v) {
	u=find(u),v=find(v); 
	fa[u]=v;
}
vector<pair<int,int>>tmp;
signed main() {
	scanf("%d%d%d", &n, &m, &k);
	for(int i = 1; i <= n; ++i) fa[i] = i;
	for(int i = 1; i <= m; ++i) {
		scanf("%d%d", &u[i], &v[i]);
		if(u[i] > v[i]) swap(u[i], v[i]);
		if(u[i] > k && v[i] > k) merge(u[i], v[i]);
	}
	for(int i = 1; i <= m; ++i) {
		if(u[i] <= k || v[i] <= k) {
			if(find(u[i]) == find(v[i])){
				ans++;
				tmp.push_back({u[i],v[i]});
			} 
			else merge(u[i], v[i]);
		}
	}
	printf("%d\n", ans);
	for(pair<int,int> p:tmp){
		cout<<p.first<<" "<<p.second<<endl; 
	}
	return 0;
}
posted @ 2025-07-25 20:13  hnczy  阅读(7)  评论(0)    收藏  举报