[POI2012]Tour de Bajtocja
[POI2012]Tour de Bajtocja
题目大意:
给定一个\(n(n\le10^6)\)个点\(m(m\le2\times10^6)\)条边的无向图,问最少删掉多少条边能使得编号小于等于\(k\)的点都不在环上,并输出任意一种删边方案。
思路:
首先若一条边两端都\(>k\),那么加上这条边对答案没有影响(就是说由它构成的环上如果有端点\(\le k\)的边,删掉后者不会更差)。
因此我们可以先将所有两端点都\(>k\)的边加上。对于有端点\(\le k\)的边,我们依次将它们加上,如果两端已经连通则不得不将这条边删去,可以证明答案是正确的。
时间复杂度\(\mathcal O(n\alpha(n))\)。
源代码:
#include<cstdio>
#include<cctype>
inline int getint() {
register char ch;
while(!isdigit(ch=getchar()));
register int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
const int N=1e6+1,M=2e6;
struct Edge {
int u,v;
};
Edge edge[M];
bool mark[M];
class DisjointSet {
private:
int anc[N];
int find(const int &x) {
return x==anc[x]?x:anc[x]=find(anc[x]);
}
public:
void reset(const int &n) {
for(register int i=1;i<=n;i++) anc[i]=i;
}
void merge(const int &x,const int &y) {
anc[find(x)]=find(y);
}
bool same(const int &x,const int &y) {
return find(x)==find(y);
}
};
DisjointSet s;
int main() {
const int n=getint(),m=getint(),k=getint();
s.reset(n);
for(register int i=0;i<m;i++) {
const int &u=edge[i].u=getint();
const int &v=edge[i].v=getint();
if(u<=k||v<=k) continue;
if(!s.same(u,v)) s.merge(u,v);
}
int ans=0;
for(register int i=0;i<m;i++) {
const int &u=edge[i].u;
const int &v=edge[i].v;
if(u>k&&v>k) continue;
if(!s.same(u,v)) {
s.merge(u,v);
} else {
ans++;
mark[i]=true;
}
}
printf("%d\n",ans);
for(register int i=0;i<m;i++) {
if(mark[i]) printf("%d %d\n",edge[i].u,edge[i].v);
}
return 0;
}