[JOISC2014]友だちをつくろう

[JOISC2014]友だちをつくろう

题目大意:

一个\(n(n\le10^5)\)个点,\(m(m\le2\times10^5)\)条边的有向图。对于两个点\(i,j\),如果存在一个点\(k\)使得存在边\(k\to i\)和边\(k\to j\),那么就新增边\(i\to j\)\(j\to i\)。不断进行上述操作,直至无边可加为止。问最终能有几条边。

思路:

用并查集维护所有的完全图。BFS将剩下能连的边连上。

源代码:

#include<queue>
#include<cstdio>
#include<cctype>
#include<vector>
#include<algorithm>
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;
}
typedef long long int64;
const int N=1e5+1;
std::vector<int> e[N];
inline void add_edge(const int &u,const int &v) {
	e[u].push_back(v);
}
struct DisjointSet {
	int anc[N],size[N];
	int find(const int &x) {
		return x==anc[x]?x:anc[x]=find(anc[x]);
	}
	void reset(const int &n) {
		std::fill(&size[1],&size[n]+1,1);
		for(register int i=1;i<=n;i++) anc[i]=i;
	}
	void merge(const int &x,const int &y) {
		size[find(y)]+=size[find(x)];
		anc[find(x)]=find(y);
	}
	bool same(const int &x,const int &y) {
		return find(x)==find(y);
	}
};
DisjointSet s;
bool vis[N];
std::queue<int> q;
int main() {
	const int n=getint(),m=getint();
	for(register int i=0;i<m;i++) {
		const int u=getint();
		add_edge(u,getint());
	}
	for(register int i=1;i<=n;i++) {
		std::sort(e[i].begin(),e[i].end());
	}
	s.reset(n);
	for(register int x=1;x<=n;x++) {
		for(register unsigned i=0;i<e[x].size();i++) {
			const int &y=e[x][i];
			const std::vector<int>::iterator p=std::lower_bound(e[y].begin(),e[y].end(),x);
			if(p==e[y].end()||*p!=x) continue;
			if(!s.same(x,y)) s.merge(x,y);
		}
	}
	for(register int x=1;x<=n;x++) {
		for(register unsigned i=1;i<e[x].size();i++) {
			const int &y=e[x][i-1],&z=e[x][i];
			if(!s.same(y,z)) s.merge(y,z);
		}
	}
	for(register int i=1;i<=n;i++) {
		if(s.size[s.find(i)]>1) {
			vis[i]=true;
			q.push(i);
		}
	}
	while(!q.empty()) {
		const int &x=q.front();
		for(register unsigned i=0;i<e[x].size();i++) {
			const int &y=e[x][i];
			if(!s.same(x,y)) s.merge(x,y);
			if(!vis[y]) {
				vis[y]=true;
				q.push(y);
			}
		}
		q.pop();
	}
	int64 ans=0;
	for(register int i=1;i<=n;i++) {
		if(i!=s.find(i)) continue;
		if(s.size[i]!=1) {
			ans+=1ll*s.size[i]*(s.size[i]-1);
		} else {
			ans+=e[i].size();
		}
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2018-12-15 13:36  skylee03  阅读(180)  评论(0编辑  收藏  举报