BZOJ1051:受欢迎的牛(并查集 / Tarjan)
1051: [HAOI2006]受欢迎的牛
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 8161 Solved: 4460
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
题解:
这题一开始我用并查集乱搞了一下,结果搞出来了...
如果一头牛受欢迎,那么他所有喜欢的牛都是被所有牛认为受欢迎的。那么我们关键就是找出第一头被所有牛认为受欢迎的牛。
在合并集合的时候用sum数组维护喜欢父亲结点的个数,当个数等于n时我们就找到一头牛了。
之后再用个bfs来求就好了。
代码如下:
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <map> #include <queue> using namespace std; const int N = 10005,M = 50005 ; int n,m,tot; int head[N],f[N],sum[N],vis[N]; map<int,map<int,int> > mp; struct Edge{ int u,v,next ; }e[M]; void adde(int u,int v){ e[++tot].u=u;e[tot].v=v; e[tot].next=head[u]; head[u]=tot; } int find(int x){ return f[x]==x ? f[x] : f[x]=find(f[x]); } int main(){ cin>>n>>m; int st=0; memset(head,-1,sizeof(head)); for(int i=1;i<=N-5;i++) f[i]=i,sum[i]=1; for(int i=1,u,v;i<=m;i++){ scanf("%d%d",&u,&v); if(!mp[u][v]){ mp[u][v]=1; adde(u,v); int fx=find(u),fy=find(v); if(fx!=fy){ f[fx]=fy; sum[fy]+=sum[fx]; if(sum[fy]==n) st=fy; } } } int cnt =1; if(!st)puts("0"); else{ queue<int> q; q.push(st);vis[st]=1; while(!q.empty()){ int u=q.front();q.pop(); for(int i=head[u];i!=-1;i=e[i].next){ int v=e[i].v; if(!vis[v]){ vis[v]=1; q.push(v); cnt++; } } } printf("%d\n",cnt); } return 0; }
再来说一下tarjan。
先用tarjan缩点,然后重新构图,找到出度为0的点那么这里面所有的牛都是被所有牛认为受欢迎的。
注意一下构图后不连通的情况就好了。
代码如下:
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <stack> using namespace std; const int N = 10005 ,M = 50005 ; int n,m,tot,num,T; int head[N],dfn[N],low[N],vis[N],scc[N]; struct Edge{ int u,v,next ; }e[M]; void adde(int u,int v){ e[++tot].u=u;e[tot].v=v; e[tot].next=head[u];head[u]=tot ; } stack <int> s; void Tanjan(int u){ dfn[u]=low[u]=++T;vis[u]=1; s.push(u); for(int i=head[u];i!=-1;i=e[i].next){ int v=e[i].v; if(!vis[v]){ Tanjan(v); low[u]=min(low[u],low[v]); }else{ low[u]=min(low[u],dfn[v]); } } if(low[u]==dfn[u]){ num++;int now; do{ now = s.top();s.pop(); scc[now]=num; }while(!s.empty() && now!=u); } } int main(){ scanf("%d%d",&n,&m); memset(head,-1,sizeof(head)); for(int i=1,u,v;i<=m;i++){ scanf("%d%d",&u,&v); adde(u,v); } for(int i=1;i<=n;i++){ if(!vis[i]) Tanjan(i); } int out[N]={0},in[N]={0}; for(int u=1;u<=n;u++){ for(int i=head[u];i!=-1;i=e[i].next){ int v=e[i].v; if(scc[u]!=scc[v]){ out[scc[u]]++; in[scc[v]]++; } } } int cnt=0,ans=0,tag; for(int i=1;i<=num;i++) if(!out[i]) cnt++,tag=i; if(cnt==1){ for(int i=1;i<=n;i++) if(scc[i]==tag) ans++; printf("%d",ans); }else puts("0"); return 0; }
重要的是自信,一旦有了自信,人就会赢得一切。