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