题目链接:http://codeforces.com/contest/505/problem/D
题解:先用tarjan缩点然后再用并查集注意下面这种情况
‘
这种情况只需要构成一个大环就行了,也就是说不需要7条边只要6条就够了。
#include <iostream> #include <cstring> #include <cstdio> #include <vector> using namespace std; const int M = 2e6 + 10; struct Edge { int v , next; }edge[M]; int head[M] , e; int Low[M] , DFN[M] , Stack[M] , Belong[M]; int scc; int Index , top; bool Instack[M]; int num[M]; void init() { memset(head , -1 , sizeof(head)); e = 0; } void add(int u , int v) { edge[e].v = v , edge[e].next = head[u] , head[u] = e++; } void Tarjan(int u) { int v; Low[u] = DFN[u] = ++Index; Stack[top++] = u; Instack[u] = true; for(int i = head[u] ; i != -1 ; i = edge[i].next) { int v = edge[i].v; if(!DFN[v]) { Tarjan(v); Low[u] = min(Low[u] , Low[v]); } else if(Instack[v]) Low[u] = min(Low[u] , DFN[v]); } if(Low[u] == DFN[u]) { scc++; do { v = Stack[--top]; Instack[v] = false; Belong[v] = scc; num[scc]++; } while(v != u); } } int f[M] , cnt[M] , flag[M]; int find(int x) { if(x == f[x]) return x; int tmp = find(f[x]); return f[x] = tmp; } int main() { int n , m; scanf("%d%d" , &n , &m); init(); for(int i = 0 ; i < m ; i++) { int u , v; scanf("%d%d" , &u , &v); add(u , v); } memset(DFN , 0 , sizeof(DFN)); memset(Instack , false , sizeof(Instack)); memset(num , 0 , sizeof(num)); memset(cnt , 0 , sizeof(cnt)); memset(flag , 0 , sizeof(flag)); scc = 0 , top = 0 , Index = 0; for(int i = 1 ; i <= n ; i++) if(!DFN[i]) Tarjan(i); for(int i = 1 ; i <= scc ; i++) {f[i] = i; if(num[i] > 1) flag[i] = 1;} for(int i = 1 ; i <= n ; i++) { for(int j = head[i] ; j != -1 ; j = edge[j].next) { int v = edge[j].v; if(Belong[i] != Belong[v]) { int a = Belong[i] , b = Belong[v]; if(a != b) { int t1 = find(a) , t2 = find(b); if(t1 != t2) { f[t2] = t1; if(num[t2] > 1 || num[t1] > 1) flag[t1] = 1; flag[t1] += flag[t2]; } } } } } int ans = 0; for(int i = 1 ; i <= scc ; i++) { ans += num[i]; f[i] = find(i); if(f[i] == i) if(!flag[i]) ans--; } printf("%d " , ans); return 0; }