1. 程式人生 > >【訓練題】強連通分量縮點 P1679

【訓練題】強連通分量縮點 P1679

Description

有 N 個人和每個人所認識人的列表,注意:即使B在A的列表中,A也不一定在B的列表中。現在小明有一個重要訊息要通知這N個人,注意:如果A認識B,則當A得到這個訊息,他就會立即通知B。


現在請你完成下面兩個任務:


任務1:請你計算要讓N個人都得到訊息,那麼小明必須把這個訊息直接通知的人的最少數目。


任務2:如果小明想要只告訴這N個人中的任何一個人,其他所有人都能得到訊息,那麼可能需要在某些人的認識列表新增認識的新成員。請你計算,最少新增多少新成員,就可以讓任何一個人得到訊息,都能傳到其他所有人。


Input

第一行包括一個整數 N:表示人數,這些人編號依次為1..N。接下來 N 行中每行都表示一個認識關係的列表,第 i+1 行包括表示第 i 人認識的人的列表,每個列表用 0 結束,空列表只用一個 0 表示。


Output

第一行包括一個正整數:任務 1 的解。第二行應該包括任務2 的解。


Hint

N<=10000


Solution

首先這道題要用tarjan縮點成一個DAG圖,之後統計入度和出度為1的點,這些點就需要一條邊來形成環。邊的數量取入度和出度為1的點的數量的最大值就可以了因為加1條邊可以分別處理一個入度為1和出度為1的點,單獨的點就需要單獨一條邊來處理。tarjan找強連通分量的原理是把點壓進棧裡進行dfs,對dfn值為0的點進行tarjan更新dfn這個時間戳,把low設定為u和v裡面low值最小的,然後當v還沒有屬於任何一個強連通分量的時候,把s的low值更新成為low[u]和dfn[v]裡的最小值。如果他們屬於同一個連通分量他們的low值都是祖先結點的low值。這個過程類似於動態規劃。當找到一條返祖邊,也就是low[s]==dfn[s]的時候,就把在s之前的點全部彈出並記錄在一個強連通分量裡直到把s彈出來。


由於圖不一定連通而一個孤點也是一個強連通分量所以只要!dfn[i]就要進行tarjan。最後列舉所有邊,要是有不在同一個強連通分量的兩個點之間有邊,起點這個強連通分量的出度就++,終點的強連通分量的入度就++。然後列舉縮點後的所有強連通分量,用ans1和ans2記錄入度為0和出度為0的點的數量,就完了。


注意事項:
1、如果所有的點都在一個強連通分量裡面,就不需要加邊,所以if(cnt==1)ans=0這個語句是必要的。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<stack>
#include<algorithm>
#define maxn 1000010
using namespace std;
struct Edge{
    int u;
    int v;
    int next;
}edge[maxn];
stack<int>stk;
int first[maxn],last[maxn],low[maxn],dfn[maxn],belong[maxn],rd[maxn],cd[maxn];
int node,n,x,hhhh,cnt,ans1,ans2,ans;
void addedge(int u,int v){
    edge[++node]=(Edge){u,v,0};
    if(first[u]==0)first[u]=node;
    else edge[last[u]].next=node;
    last[u]=node;
    return;
}
void init(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        while(1){
            scanf("%d",&x);
            if(x==0)break;
            addedge(i,x);
        }
    }
    return;
}
void tarjan(int s){
    low[s]=dfn[s]=++hhhh;
    stk.push(s);
    for(int q=first[s];q;q=edge[q].next){
        int p=edge[q].v;
        if(dfn[p]==0){
            tarjan(p);
            low[s]=min(low[s],low[p]);
        }
        else if(!belong[p]){
            low[s]=min(low[s],dfn[p]);
        }
    }
    if(low[s]==dfn[s]){
        cnt++;
        belong[s]=cnt;
        while(stk.top()!=s){
            belong[stk.top()]=cnt;
            stk.pop();
        }
        stk.pop();
    }
}
void solve(){
    for(int i=1;i<=n;i++){
        if(!dfn[i]){
            tarjan(i);
        }
    }
    for(int i=1;i<=n;i++){
        for(int q=first[i];q;q=edge[q].next){
            int p=edge[q].v;
            if(belong[i]!=belong[p]){
                rd[belong[p]]++;
                cd[belong[i]]++;
            }
        }
    }
    for(int i=1;i<=cnt;i++){
        if(rd[i]==0)ans1++;
        if(cd[i]==0)ans2++;
    }
}
int main(){
    init();
    solve();
    printf("%d\n",ans1);
    ans=max(ans1,ans2);
    if(cnt==1)ans=0;
    printf("%d\n",ans);
    return 0;
}