1. 程式人生 > >Codevs2822愛在心中題解

Codevs2822愛在心中題解

題目

  • 來源
  • 題目描述 Description
    “每個人都擁有一個夢,即使彼此不相同,能夠與你分享,無論失敗成功都會感動。愛因為在心中,平凡而不平庸,世界就像迷宮,卻又讓我們此刻相逢Our Home。”
    在愛的國度裡有N(N1000)個人,在他們的心中都有著一個愛的名單,上面記載著他所愛的人(不會出現自愛的情況)。愛是具有傳遞性的,即如果A愛B,B愛C,則A也愛C。
    如果有這樣一部分人,他們彼此都相愛,則他們就超越了一切的限制,用集體的愛化身成為一個愛心天使。
    現在,我們想知道在這個愛的國度裡會出現多少愛心天使。而且,如果某個愛心天使被其他所有人或愛心天使所愛則請輸出這個愛心天使是由哪些人構成的,否則輸出-1。

  • 輸入描述 Input Description
    第1行,兩個數N、M,代表愛的國度裡有N個人,愛的關係有M(M10000)條。
    第2到第M+1行,每行兩個數A、B,代表A愛B。

  • 輸出描述 Output Description
    第1行,一個數,代表愛的國度裡有多少愛心天使。
    第2行,如果某個愛心天使被其他所有人和愛心天使所愛則請輸出這個愛心天使是由哪些人構成的(從小到大排序),否則輸出-1。

  • 樣例輸入 Sample Input
    樣例輸入1:
    6 7
    1 2
    2 3
    3 2
    4 2
    4 5
    5 6
    6 4
    樣例輸入2:
    3 3
    1 2
    2 1
    2 3

  • 樣例輸出 Sample Output


    樣例輸出1:
    2
    2 3
    樣例輸出2:
    1
    -1

  • 資料範圍及提示 Data Size & Hint
    各個測試點1s

題解

  • 第一問,不難想到是直接強連通分量縮點,再記錄強連通分量的大小。愛心天使是包含結點個數大於等於2的強連通分量。權當練一發非遞迴tarjan了。

  • 第二問,初看起來有些複雜,但深入分析一下發現,其實也就那麼回事。
    縮完點後,肯定不存在環。若存在一個被所有人愛的愛心天使,那在有向圖中體現為所有的點都直接或間接指向這個強連通分量,這個強連通分量不能有出邊並且有且只有一個,證明如下:
    1、若有某點不指向這個強連通分量,那由定義,這個強連通分量不是答案;
    2、若這個強連通分量有出邊,設這條出邊指向的終點為t,由1,t不可能不指向這個強連通分量本身,那這個強連通分量就包含點t,出現矛盾,故這個強連通分量不可能有出邊;
    3、若這類強連通分量不唯一,那由2,這些強連通分量都沒有出邊,它們無法相連,這個愛心天使就不被所有人愛,不合題意,故若有這類強連通分量,有且只有一個。
    那我們tarjan縮完點後,記錄每個強連通分量的出度。之後列舉所有強連通分量,記錄出度為零的強連通分量的個數。若最終個數不為1或者其大小不大於1,則無解;否則輸出這個強連通分量的所有點。

  • Code

#include <cstdio>
#include <algorithm>
#include <cstring>
#define N 1005
#define M 10005
#define nil 0
using namespace std;
int n, m, oud[N];
int u[M], v[M], nxt[M], pnt[N], e;
int s[N], top;
int S[N], TOP, lst[N];
int dfn[N], low[N], isin[N], sz[N], tot, indx;
bool ins[N];
inline void add(int a, int b)
{
    u[++e] = a; v[e] = b; nxt[e] = pnt[a]; pnt[a] = e;
}
void tarjan(int x)
{
    dfn[x] = low[x] = ++indx;
    S[++TOP] = s[++top] = x;
    ins[x] = true;
    while(TOP > 0)
    {
        int t = S[TOP];
        for(int i = lst[t]; i != nil; i = nxt[i])
        {
            if(dfn[v[i]] == 0)
            {
                lst[t] = nxt[i];
                dfn[v[i]] = low[v[i]] = ++indx;
                S[++TOP] = s[++top] = v[i];
                ins[v[i]] = true;
                break;
            }
        }
        if(t == S[TOP])
        {
            for(int i = pnt[t]; i != nil; i = nxt[i])
            {
                if(dfn[v[i]] > dfn[t]) low[t] = min(low[t], low[v[i]]);
                else if(ins[v[i]]) low[t] = min(low[t], dfn[v[i]]);
            }
            if(dfn[t] == low[t])
            {
                int j;
                ++tot;
                do
                {
                    j = s[top--];
                    ins[j] = false;
                    isin[j] = tot;
                    ++sz[tot];
                } while(j != t);
            }
            --TOP;
        }
    }
}
void init()
{
    e = top = TOP = tot = indx = 0;
    memset(u, 0, sizeof(u));
    memset(v, 0, sizeof(v));
    memset(nxt, 0, sizeof(nxt));
    memset(pnt, 0, sizeof(pnt));
    memset(dfn, 0, sizeof(dfn));
    memset(low, 0, sizeof(low));
    memset(isin, 0, sizeof(isin));
    memset(sz, 0, sizeof(sz));
    memset(ins, 0, sizeof(ins));
    memset(lst, 0, sizeof(lst));
    memset(oud, 0, sizeof(oud));
    int a, b;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; ++i)
    {
        scanf("%d%d", &a, &b);
        add(a, b);
    }
    for(int i = 1; i <= n; ++i) lst[i] = pnt[i];
}
void work()
{
    for(int i = 1; i <= n; ++i) if(dfn[i] == 0)
    {
        tarjan(i);
    }
    int ans = 0;
    for(int i = 1; i <= tot; ++i)
    {
        if(sz[i] > 1) ++ans;
    }
    printf("%d\n", ans);
    for(int i = 1; i <= n; ++i) for(int j = pnt[i]; j != nil; j = nxt[j])
    {
        if(isin[i] != isin[v[j]]) ++oud[isin[i]];
    }
    int cnt = 0, poi = 0;
    for(int i = 1; i <= tot; ++i) if(oud[i] == 0)
    {
        ++cnt; poi = i;
    }
    if(cnt != 1 || sz[poi] <= 1) puts("-1");
    else
    {
        for(int i = 1; i <= n; ++i)
        {
            if(isin[i] == poi) printf("%d ", i);
        }
    }
}
int main()
{
    init();
    work();
    return 0;
}