洛谷 P2341 BZOJ 1051 [HAOI2006]受歡迎的牛

題目描述

每頭奶牛都夢想成爲牛棚裏的明星。被全部奶牛喜歡的奶牛就是一頭明星奶牛。全部奶算法

牛都是自戀狂,每頭奶牛老是喜歡本身的。奶牛之間的「喜歡」是能夠傳遞的——若是A喜數組

歡B,B喜歡C,那麼A也喜歡C。牛欄裏共有N 頭奶牛,給定一些奶牛之間的愛慕關係,請你函數

算出有多少頭奶牛能夠當明星。學習

輸入輸出格式

輸入格式:spa

 

 第一行:兩個用空格分開的整數:N和Mcode

 第二行到第M + 1行:每行兩個用空格分開的整數:A和B,表示A喜歡Bblog

 

輸出格式:博客

 

 第一行:單獨一個整數,表示明星奶牛的數量it

 

輸入輸出樣例

輸入樣例#1:
3 3
1 2
2 1
2 3
輸出樣例#1:
1

說明

只有 3 號奶牛能夠作明星io

【數據範圍】

10%的數據N<=20, M<=50

30%的數據N<=1000,M<=20000

70%的數據N<=5000,M<=50000

100%的數據N<=10000,M<=50000

 

 

感想

2019年8月16日12:39:48:看本身博客學新算法

最近在糾結寫博客那麼詳細會不會太浪費時間了,畢竟只是給本身或者周圍人看的……但今天再次開始搞tarjan,發現本身就寫了這一篇文章,還那麼簡略……以致於還要去其餘博客學習……不過我看出來了,當時的代碼裏,b數組不必,判斷每一條邊時使用e數組就好

2019年8月16日13:27:54 從新寫了一遍,WA了,緣由是tarjan函數裏,把u點入棧之後忘記更新instack數組了。加上就A了(但出棧的時候仍是忘記更新instack數組了,因此第二份代碼是有鍋的。不過也A了,這究竟是不必,仍是數據水……留坑)。重寫畫了半個小時,好長啊,中途還磕磕絆絆,好比tarjan內部的幾種判斷的順序啥的……

解題思路

  tarjan找到強連通份量,而後縮點,統計縮了以後每一個強連通份量的出度,若是隻有一個出度爲零的強連通份量,答案就是這個強連通份量裏點的個數;若是出度爲零的強連通份量不止一個,那麼答案就爲0。

  記得Neil作這題的時候曾經疑惑過——若是縮點後獲得的圖仍是成環咋辦?那麼全部強連通份量出度都不爲零了,答案應該爲0,但事實上應該全部奶牛都受歡迎了……原來這個算法正確性是這麼保證的——tarjan算法有一個性質:求出的強連通份量必定是極大強連通份量,因此縮出來的點確定不會成環。

源代碼

2017年7月2日的

#include<cstdio>
#include<algorithm>

int n,m;

struct edge{
    int u,v;
}b[100010];

struct Edge{
    int nxt,to;
}e[100010];
int head[100010]={0},cnt=1;
void add(int u,int v)
{
    e[cnt]={head[u],v};
    head[u]=cnt++;
}

int id[100010]={0},index=0;
int num[100010]={0};

int dfn[100010]={0},low[100010]={0},dfs_time=0;
int stack[100010]={0},top=0;
bool instack[100010]={0};
void tarjan(int u)
{
    low[u]=dfn[u]=++dfs_time;
    stack[top++]=u;
    instack[u]=1;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int v=e[i].to;
        if(!dfn[v])
            tarjan(v),low[u]=std::min(low[v],low[u]);
        else if(instack[v])
            low[u]=std::min(low[v],low[u]);
    }
    if(dfn[u]==low[u])
    {
        index++;
        int v;
        do{
            v=stack[--top];
            stack[top]=0;
            id[v]=index,instack[v]=0;
            num[index]++;
        }while(v!=u);
    }
}

int out[100010]={0};

int main()
{
    //freopen("cow.in","r",stdin);
    //freopen("cow.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1,u,v;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v);
        b[i]={u,v};
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i])
            tarjan(i);
    for(int i=1;i<=m;i++)
        if(id[b[i].u]!=id[b[i].v])
            out[id[b[i].u]]++;

    
    int ans=0;
    for(int i=1;i<=index;i++)
        if(!out[i])
        {
            if(ans)
            {
                printf("0\n");
                return 0;
            }
            else ans=num[i];
        }
    printf("%d\n",ans);
    return 0;
}

 2019年8月16日13:39:02更新——

 1 #include<cstdio>
 2 #include<algorithm>
 3 
 4 const int MAXN=1e4+5,MAXM=5e4+5;
 5 
 6 int n,m;
 7 
 8 struct Edge{
 9     int nxt,to;
10 }e[MAXM];
11 int cnt=1,head[MAXN];
12 inline void add(int u,int v)
13 {
14     e[cnt]={head[u],v};
15     head[u]=cnt++;
16 }
17 
18 int dfn[MAXN],low[MAXN],dfst=1;//時間戳們
19 int stack[MAXN],top=0;
20 bool instack[MAXN];
21 int id[MAXN],index=1;//縮點用的新id
22 int num[MAXN];//統計每一個強連通份量大小
23 int out[MAXN];//統計各強連通份量出度
24 void tarjan(int u)
25 {
26     dfn[u]=low[u]=dfst++;//打時間戳
27     stack[++top]=u;//入棧
28     instack[u]=1;
29     for(int i=head[u];i;i=e[i].nxt)
30     {
31         int v=e[i].to;
32         if(!dfn[v])//樹邊
33         {
34             tarjan(v);
35             low[u]=std::min(low[u],low[v]);
36         }
37         else if(instack[v])//返祖邊
38         {
39             low[u]=std::min(low[u],low[v]);//暫時先更新low,不急着出棧,以便找到極大強連通份量
40         }
41     }
42     if(low[u]==dfn[u])//得割點一個,出棧縮點
43     {
44         do//爲啥都在棧裏來着?
45         {
46             id[stack[top--]]=index;
47             num[index]++;//這句話能夠放在外面O(1)處理
48         }while(stack[top+1]!=u);
49         index++;
50     }
51 }
52 
53 int main()
54 {
55     scanf("%d%d",&n,&m);
56     for(int i=1,u,v;i<=m;i++)
57     {
58         scanf("%d%d",&u,&v);
59         add(u,v);
60     }
61     for(int i=1;i<=n;i++)
62     {
63         if(!dfn[i]) tarjan(i);
64     }
65 
66     for(int u=1;u<=n;u++)//統計出度
67     {
68         for(int i=head[u];i;i=e[i].nxt)
69         {
70             int v=e[i].to;
71             if(id[u]!=id[v])
72             {
73                 out[id[u]]++;
74             }
75         }
76     }
77     int ans=0;
78     for(int i=1;i<index;i++)
79     {
80         if(!out[i])
81         {
82             if(ans)
83             {
84                 puts("0");
85                 return 0;
86             }
87             ans=num[i];
88         }
89     }
90     printf("%d\n",ans);
91     return 0;
92 }
相關文章
相關標籤/搜索