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

浙公网安备 33010602011771号