BZOJ 1051 最受欢迎的牛 解题报告
题目直接摆在这里!
1051: [HAOI2006]受欢迎的牛
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 4438 Solved: 2353
[Submit][Status][Discuss]
Description
每一头牛的愿望就是变成一头最受欢迎的牛。现在有N头牛,给你M对整数(A,B),表示牛A认为牛B受欢迎。 这
种关系是具有传递性的,如果A认为B受欢迎,B认为C受欢迎,那么牛A也认为牛C受欢迎。你的任务是求出有多少头
牛被所有的牛认为是受欢迎的。
Input
第一行两个数N,M。 接下来M行,每行两个数A,B,意思是A认为B是受欢迎的(给出的信息有可能重复,即有可
能出现多个A,B)
Output
一个数,即有多少头牛被所有的牛认为是受欢迎的。
Sample Input
3 3
1 2
2 1
2 3
1 2
2 1
2 3
Sample Output
1
HINT
100%的数据N<=10000,M<=50000
Source
这道题和codevs2822爱在心中类似,也是一道tarjan练手题,所以具体的tarjan板子我就不再贴出来了。
这道题的思路也可以和codevs2822一样的,使用tarjan+SPFA,但是由于本人太懒,不想再打140行代码,就换了另一种思路。
正解:tarjan缩点
具体做法:
第一步,先读入数据建边;
第二步,开始跑tarjan,别忘记在tarjan过程中要缩点,接下来的过程要用到。(所谓缩点,就是把每个点都归到一个强联通块里面,对强联通块进行操作)
第三步:根据m个边的关系,统计每个强联通块的出度。
第四步:统计出度为0的强联通块的个数,若为1,则输出次强联通块组成元素个数,否则输出0。(这里才是这道题目核心的思想,想一想为什么,其实很简单)。
先贴上代码(建议大家不要用vector这种东西,数据结构尽量手写)
1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <cstring> 5 #include <cstdlib> 6 #include <algorithm> 7 #include <stack> 8 using namespace std; 9 int get_num(){ 10 int num = 0; 11 char c; 12 bool flag = false; 13 while((c = getchar()) == ' ' || c == '\r' || c == '\n'); 14 if(c == '-') 15 flag = true; 16 else num = c - '0'; 17 while(isdigit(c = getchar())) 18 num = num * 10 + c - '0'; 19 return (flag ? -1 : 1) * num; 20 } 21 const int maxn = 1e4 + 5; 22 const int maxm = 5e4 + 5; 23 int n,m,h[maxn],sccno[maxn],scc_cnt,id[maxn],sum,r[maxn],dfn[maxn],low[maxn],dfs_clock,ans,pos; 24 stack<int>s; 25 struct edge{ 26 int fr,to,next; 27 }edges[maxm << 1]; 28 void addedge(int u,int v){ 29 edges[sum].fr = u; 30 edges[sum].to = v; 31 edges[sum].next = h[u]; 32 h[u] = sum++; 33 } 34 void init(){ 35 memset(id,0,sizeof(id)); 36 memset(sccno,0,sizeof(sccno)); 37 memset(r,0,sizeof(r)); 38 memset(h,-1,sizeof(h)); 39 memset(edges,0,sizeof(edges)); 40 sum = 0; 41 dfs_clock = 0; 42 memset(dfn,0,sizeof(dfn)); 43 memset(low,0,sizeof(low)); 44 } 45 void tarjan(int u){ 46 dfn[u] = low[u] = ++dfs_clock; 47 s.push(u); 48 for(int i = h[u];i != -1;i = edges[i].next){ 49 edge e = edges[i]; 50 if(!dfn[e.to]){ 51 tarjan(e.to); 52 low[u] = min(low[u],low[e.to]); 53 } 54 else if(!id[e.to]) 55 low[u] = min(low[u],dfn[e.to]); 56 } 57 if(low[u] == dfn[u]){ 58 scc_cnt++; 59 while(true){ 60 int x = s.top(); 61 s.pop(); 62 sccno[scc_cnt] += 1; 63 id[x] = scc_cnt; 64 if(x == u)break; 65 } 66 } 67 return; 68 } 69 void find_tarjan(){ 70 for(int i = 1;i <= n;++i){ 71 if(!dfn[i]) 72 tarjan(i); 73 } 74 return; 75 } 76 int main(){ 77 int a,b; 78 init(); 79 n = get_num(); 80 m = get_num(); 81 for(int i = 1;i <= m;++i){ 82 a = get_num(); 83 b = get_num(); 84 addedge(a,b); 85 } 86 find_tarjan(); 87 for(int i = 0;i < sum;++i){ 88 int x = edges[i].fr; 89 int y = edges[i].to; 90 if(id[x] != id[y]) 91 r[id[x]]++; 92 } 93 ans = 0; 94 for(int i = 1;i <= scc_cnt;++i){ 95 if(r[i] == 0){ 96 if(!ans) 97 ans = sccno[i]; 98 else{ 99 ans = 0; 100 break; 101 } 102 } 103 } 104 printf("%d\n",ans); 105 return 0; 106 }
这次解题报告就写到这里吧,NOIP倒计时两个月,祝大家能考个好成绩!