每頭奶牛都夢想成爲牛棚裏的明星。被全部奶牛喜歡的奶牛就是一頭明星奶牛。全部奶算法
牛都是自戀狂,每頭奶牛老是喜歡本身的。奶牛之間的「喜歡」是能夠傳遞的——若是A喜數組
歡B,B喜歡C,那麼A也喜歡C。牛欄裏共有N 頭奶牛,給定一些奶牛之間的愛慕關係,請你函數
算出有多少頭奶牛能夠當明星。學習
輸入格式:spa
第一行:兩個用空格分開的整數:N和Mcode
第二行到第M + 1行:每行兩個用空格分開的整數:A和B,表示A喜歡Bblog
輸出格式:博客
第一行:單獨一個整數,表示明星奶牛的數量it
3 3 1 2 2 1 2 3
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 }