有 n 个点互不相连,现有 m 个单向连通性要求,问最少需要多少条单向边。
将 m 个连通性要求当做双向边建并查集找到所有的连通块,对每个集合,如果不存在环,这个连通块的答案是 |V|−1 ,否则这个连通块的答案是 |V| 。
// by ztx
#include
#include
#define maxn 100010LL
int e[2][maxn], star[maxn] = {0}, tote = 0;
#define to(p) e[0][p]
#define nxt(p) e[1][p]
inline void AddEdge(int u,int v) {
tote ++ , to(tote) = v, nxt(tote) = star[u], star[u] = tote;
}
int fa[maxn] = {0};
inline int GetAnc(int u) { return fa[u] ? fa[u]=GetAnc(fa[u]) : u; }
inline void Union(int u,int v) {
u = GetAnc(u), v = GetAnc(v);
if (u != v) fa[u] = v;
}
int ind[maxn] = {0}, poi[maxn];
inline bool cmp(const int&a,const int&b) {
return fa[a]==fa[b] ? ind[a] < ind[b] : fa[a] < fa[b];
}
int sta[maxn], top;
inline bool Circle(int L,int R) {
int i, u, p;
top = 0;
for (i = L; i <= R; i ++ )
if (ind[poi[i]] == 0) sta[++top] = poi[i];
else break;
while (top) {
u = sta[top--];
for (p = star[u]; p; p=nxt(p))
if ((--ind[to(p)]) == 0) sta[++top] = to(p);
}
for (i = R; i >= L; i -- ) if (ind[poi[i]] > 0) return true;
return false;
}
int n, m;
int main() {
int i, u, v, ans, last;
scanf("%d%d", &n, &m);
for (i = 1; i <= m; i ++ ) {
scanf("%d%d", &u, &v);
AddEdge(u,v), Union(u,v), ind[v] ++ ;
}
for (i = 1; i <= n; i ++ )
GetAnc(i), poi[i] = i;
for (i = 1; i <= n; i ++ )
if (!fa[i]) fa[i] = i;
std::sort(poi+1,poi+n+1,cmp);
ans = 0, last = 1;
poi[n+1] = n+1, fa[n+1] = -1;
for (i = 1; i <= n; i ++ )
if (fa[poi[i]] != fa[poi[i+1]]) {
if (Circle(last,i)) ans += i-last+1;
else ans += i-last;
last = i+1;
}
printf("%d\n", ans);
//while(1);
return 0;
}