codeforce 505 D. Mr. Kitayuta's Technology(tarjan+并查集)

题目链接: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\n" , ans);
    return 0;
}
posted @ 2017-05-19 01:05  Gealo  阅读(185)  评论(0编辑  收藏  举报