【BZOJ】1051: [HAOI2006]受欢迎的牛
[HAOI2006]受欢迎的牛
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
思路:开始认为可以使用并查集,但是由于给个点有多个父节点,朴素的并查集不行;
这样就需要建图,使用Tarjan对强连通分量进行缩点,之后以缩点作为并查集的元素(只是思想,因为答案要求的只是一部分,所以只看出度即可),看是否只有一个根节点(每一个缩点是环,只要有一个点有一条指向另一个缩点的有向边,那么该缩点中所有的元素都是所指向缩点中点的粉丝);这样只需说明只有一个点出度为0;即只有一个根节点即可;那么该根节点所代表的缩点中点数就是answer;
ps:开始认为Tarjan之后对每条边进行遍历;(受hdu2767 Proving Equivalences添边构造强连通图的影响)当边为两个连通分量(缩点)的连边时,就将缩点所代表的点个数进行累加,之后对所有点代入到所在的联通分量(belong)中,看是否集合大小为n(包含了自己);但是这种思想是错误的。因为分析的时候只是看了单独的两个缩点之间的关系;即使使用set排重,但是只是两个缩点之间可以实现排重。这样如果扩展到三个缩点就会发现会出现重复计算;
/******************************************************** Time:88 ms Memory:2888 kb ****************************************************************/ #include<iostream> #include<cstdio> #include<stack> #include<cstring> using namespace std; #define rep0(i,l,r) for(int i = (l);i < (r);i++) #define rep1(i,l,r) for(int i = (l);i <= (r);i++) #define rep_0(i,r,l) for(int i = (r);i > (l);i--) #define rep_1(i,r,l) for(int i = (r);i >= (l);i--) #define MS0(a) memset(a,0,sizeof(a)) #define MS1(a) memset(a,-1,sizeof(a)) const int MAXN = 50050; int head[MAXN],tot; struct edge{ int to,w,Next; }e[MAXN]; void ins(int a,int b,int w = 0) { e[++tot].Next = head[a]; e[tot].to = b; e[tot].w = w; head[a] = tot; } const int N = 10020; int pre[N],dfs_clock,low[N]; int belong[N],scc,num[N]; stack<int> S; bool stk[N]; void Tarjan(int u) { pre[u] = low[u] = ++dfs_clock; S.push(u); stk[u] = true; int v;//点u所在连通分量的出度; for(int i = head[u];i;i = e[i].Next){ v = e[i].to; if(pre[v] == 0){ Tarjan(v); low[u] = min(low[u],low[v]); }else if(stk[v]){ low[u] = min(low[u],pre[v]); } } if(pre[u] == low[u]){//强连通分量的根节点 ++scc; do{ v = S.top(); S.pop();stk[v] = false; belong[v] = scc; num[scc]++; }while(v != u); } } int id[MAXN]; int main() { int T,kase = 1; MS0(head);tot = 0; int n,m,a,b; scanf("%d%d",&n,&m); rep0(i,0,m){ scanf("%d%d",&a,&b); ins(a,b); } scc = dfs_clock = 0; rep1(i,0,n) pre[i] = low[i] = num[i] = belong[i] = 0; rep1(i,1,n)if(pre[i] == 0) Tarjan(i); int ans = 0;MS0(id); rep1(u,1,n){ for(int index = head[u];index;index = e[index].Next){ int v = e[index].to; int bv = belong[v],bu = belong[u]; if(bv != bu && id[bu] == 0){//***强连通分量之间的连边 id[bu]++;//标记出度不为0的点 } } } rep1(i,1,scc) if(id[i] == 0){ if(ans) return puts("0"),0; ans = num[i]; } printf("%d\n",ans); return 0; }