Codeforces Round #286 (Div. 2) D. Mr. Kitayuta's Technology 強連通分量 有向圖求環

D. Mr. Kitayuta's Technology
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

Shuseki Kingdom is the world's leading nation for innovation and technology. There are n cities in the kingdom, numbered from 1 to n.

Thanks to Mr. Kitayuta's research, it has finally become possible to construct teleportation pipes between two cities. A teleportation pipe will connect two cities unidirectionally, that is, a teleportation pipe from city x to city y cannot be used to travel from city y to city x. The transportation within each city is extremely developed, therefore if a pipe from city x to city y and a pipe from city y to city z are both constructed, people will be able to travel from city x to city z instantly.

Mr. Kitayuta is also involved in national politics. He considers that the transportation between the m pairs of city (ai, bi) (1 ≤ i ≤ m) is important. He is planning to construct teleportation pipes so that for each important pair (ai, bi), it will be possible to travel from city ai to city bi by using one or more teleportation pipes (but not necessarily from city bi to city ai). Find the minimum number of teleportation pipes that need to be constructed. So far, no teleportation pipe has been constructed, and there is no other effective transportation between cities.

Input

The first line contains two space-separated integers n and m (2 ≤ n ≤ 105, 1 ≤ m ≤ 105), denoting the number of the cities in Shuseki Kingdom and the number of the important pairs, respectively.

The following m lines describe the important pairs. The i-th of them (1 ≤ i ≤ m) contains two space-separated integers ai and bi(1 ≤ ai, bi ≤ n, ai ≠ bi), denoting that it must be possible to travel from city ai to city bi by using one or more teleportation pipes (but not necessarily from city bi to city ai). It is guaranteed that all pairs (ai, bi) are distinct.

Output

Print the minimum required number of teleportation pipes to fulfill Mr. Kitayuta's purpose.

Sample test(s)
input
4 5
1 2
1 3
1 4
2 3
2 4
output
3
input
4 6
1 2
1 4
2 3
2 4
3 2
3 4
output
4
Note

For the first sample, one of the optimal ways to construct pipes is shown in the image below:

For the second sample, one of the optimal ways is shown below:

題意,給出一序列有序對,要求用最少的邊很得給出的有序對之間能連通。

首先,按無向的圖來看,分割成連通塊,連通塊之間,是沒有關係的。對於某個連通塊,如果,有向連通圖沒有環,用拓撲排序,就可以排出滿足的所有要求,只要用n-1個邊(拓撲排序的過程,就滿足了所有的要求),如果是有環的連通圖,只需要用n個點就可 以了(n個點首尾相連,就可以滿足所有的要求)。如何判定有向連通圖是否有環呢,只需要,某個點其對應的強連通分量大於2,就說明有環。總的複雜度爲求強連通分量o(n + m );

有向圖求環,有兩種方法

1.拓撲排序,能拓撲排序,自然沒環

2.強連通分量,如果大於2結點數,有環,複雜度爲o(n + m );適合於本題

#define N 100005
#define M 200005
#define maxn 205
#define MOD 1000000000000000007
//強連通模版
struct Graph{
    //N爲結點數 Stap 棧 Stop棧最大值 Dclock 時針號  Belong屬於哪個Bcnt連通分量的個數
    int DFN[N],LOW[N],Stap[N],Stop,Dclock,Belong[N],Bcnt,n;
    //是否在棧中
    bool instack[N];
    vector<pii> p[N];
    void tarjan(int i)
    {
        int j;
        DFN[i]=LOW[i]=++Dclock;
        instack[i]=true;
        Stap[++Stop]=i;
        for(int k = 0;k < p[i].size();k++)
        {
            j = p[i][k].first;
            if (!DFN[j])
            {
                tarjan(j);
                if (LOW[j]<LOW[i])
                    LOW[i]=LOW[j];
            }
            else if (instack[j] && DFN[j]<LOW[i])
                LOW[i]=DFN[j];
        }
        if (DFN[i]==LOW[i])
        {
            Bcnt++;
            do
            {
                j=Stap[Stop--];
                instack[j]=false;
                Belong[j]=Bcnt;
            }
            while (j!=i);
        }
    }
    //求強連通
    void StrongConnected()
    {
        int i;
        Stop=Bcnt=Dclock=0;
        memset(DFN,0,sizeof(DFN));
        memset(LOW,0,sizeof(LOW));
        memset(instack,false,sizeof(instack));
        for (i=1;i<=n;i++)
            if (!DFN[i])
                tarjan(i);
    }
    //從1開始計數
    void init(int nn){
        n = nn;
        FI(n + 1)
            p[i].clear();
    }
    void AddEdge(int a,int b,int c){
        p[a].push_back(mp(b,c));
    }

    //擴展功能 統計每個強連通分量的個數
    int CNum[N];
    void GetCount(){
        memset(CNum,0,sizeof(CNum));
        for (int i=1;i<=n;i++)
            CNum[Belong[i]]++;
    }
}G1,G2;

//本題要用的
int n,m,a,b,ans,flag;
bool Vis[N];
void DFS(int x){
    Vis[x] = true;
    if(G1.CNum[G1.Belong[x]] >= 2){
        flag++;
    }
    ans++;
    FI(G2.p[x].size()){
        int goal = G2.p[x][i].first;
        if(!Vis[goal])
            DFS(goal);
    }
}
int main()
{
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
     while(S2(n,m)!=EOF)
    {
        G1.init(n);
        G2.init(n);
        FI(m){
            S2(a,b);
            G1.AddEdge(a,b,0);
            G2.AddEdge(a,b,0);
            G2.AddEdge(b,a,0);
        }
        G1.StrongConnected();
        G1.GetCount();
        /*
        for(int i = 1;i<=n;i++){
            printf(" %d ",G1.Belong[i]);
        }
        cout<<endl;
        */
        ans = 0;
        fill(Vis,false);
        for (int i=1;i<=n;i++){
            if(!Vis[i]){
                flag = 0;
                DFS(i);
                if(!flag)
                    ans--;
                //printf("%d %d\n",flag,ans);
            }
        }
        printf("%d\n",ans);
    }
    //fclose(stdin);
    //fclose(stdout);
    return 0;
}

強連通分量的模板

//強連通模版
struct Graph{
    //N爲結點數 Stap 棧 Stop棧最大值 Dclock 時針號  Belong屬於哪個Bcnt連通分量的個數
    int DFN[N],LOW[N],Stap[N],Stop,Dclock,Belong[N],Bcnt,n;
    //是否在棧中
    bool instack[N];
    vector<pii> p[N];
    void tarjan(int i)
    {
        int j;
        DFN[i]=LOW[i]=++Dclock;
        instack[i]=true;
        Stap[++Stop]=i;
        for(int k = 0;k < p[i].size();k++)
        {
            j = p[i][k].first;
            if (!DFN[j])
            {
                tarjan(j);
                if (LOW[j]<LOW[i])
                    LOW[i]=LOW[j];
            }
            else if (instack[j] && DFN[j]<LOW[i])
                LOW[i]=DFN[j];
        }
        if (DFN[i]==LOW[i])
        {
            Bcnt++;
            do
            {
                j=Stap[Stop--];
                instack[j]=false;
                Belong[j]=Bcnt;
            }
            while (j!=i);
        }
    }
    //求強連通
    void StrongConnected()
    {
        int i;
        Stop=Bcnt=Dclock=0;
        memset(DFN,0,sizeof(DFN));
        memset(LOW,0,sizeof(LOW));
        memset(instack,false,sizeof(instack));
        for (i=1;i<=n;i++)
            if (!DFN[i])
                tarjan(i);
    }
    //從1開始計數
    void init(int nn){
        n = nn;
        FI(n + 1)
            p[i].clear();
    }
    void AddEdge(int a,int b,int c){
        p[a].push_back(mp(b,c));
    }

    //擴展功能 統計每個強連通分量的個數
    int CNum[N];
    void GetCount(){
        memset(CNum,0,sizeof(CNum));
        for (int i=1;i<=n;i++)
            CNum[Belong[i]]++;
    }
}G1,G2;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章