[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;
}
posted @ 2018-10-30 20:29  skylee03  阅读(123)  评论(0编辑  收藏  举报