Tarjan模板题——牛的舞会
题目描述
约翰的N (2 <= N <= 10,000)只奶牛非常兴奋,因为这是舞会之夜!她们穿上礼服和新鞋子,别 上鲜花,她们要表演圆舞.
只有奶牛才能表演这种圆舞.圆舞需要一些绳索和一个圆形的水池.奶牛们围在池边站好, 顺时针顺序由1到N编号.每只奶牛都面对水池,这样她就能看到其他的每一只奶牛.
为了跳这种圆舞,她们找了 M(2<M< 50000)条绳索.若干只奶牛的蹄上握着绳索的一端, 绳索沿顺时针方绕过水池,另一端则捆在另一些奶牛身上.这样,一些奶牛就可以牵引另一些奶 牛.有的奶牛可能握有很多绳索,也有的奶牛可能一条绳索都没有.
对于一只奶牛,比如说贝茜,她的圆舞跳得是否成功,可以这样检验:沿着她牵引的绳索, 找到她牵引的奶牛,再沿着这只奶牛牵引的绳索,又找到一只被牵引的奶牛,如此下去,若最终 能回到贝茜,则她的圆舞跳得成功,因为这一个环上的奶牛可以逆时针牵引而跳起旋转的圆舞. 如果这样的检验无法完成,那她的圆舞是不成功的.
如果两只成功跳圆舞的奶牛有绳索相连,那她们可以同属一个组合.
给出每一条绳索的描述,请找出,成功跳了圆舞的奶牛有多少个组合?
输入输出格式
输入格式:
Line 1: Two space-separated integers: N and M
Lines 2..M+1: Each line contains two space-separated integers A and B that describe a rope from cow A to cow B in the clockwise direction.
输出格式:
Line 1: A single line with a single integer that is the number of groups successfully dancing the Round Dance.
输入输出样例
输入:
5 4
2 4
3 5
1 2
4 1
输出:
1
这道题思路还是很简单的 一个裸的tarjan强连通分量 在这里还是着重解释一下tarjan,代码如下:
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; #define maxn 100010 int n,m; int cnt;//记录强连通分量的个数 int vis_num;//遍历的步数 int dfn[maxn];//记录元素第一次被访问的步数 int low[maxn];//包含i的强连通分量最早被访问的步数 int num[maxn];//记录强连通分量里的点的个数 int belong[maxn];//i从属的强连通分量 int top;//栈中元素的个数 int stack[maxn];//手打栈 int instack[maxn];//判断元素是否在栈中 int head[maxn]; struct node//链式前向星存边 { int to,nex; }edge[maxn]; inline int read()//读入优化 { char kr=0; char ls; for(;ls>'9'||ls<'0';kr=ls,ls=getchar()); int xs=0; for(;ls>='0'&&ls<='9';ls=getchar()) { xs=xs*10+ls-48; } if(kr=='-') xs=0-xs; return xs; } void tarjan(int); int main() { int ans=0; int p,q; n=read();m=read(); for(int i=1;i<=m;i++) { p=read();q=read(); edge[i].to=q; edge[i].nex=head[p]; head[p]=i; } for(int i=1;i<=n;i++) { if(!dfn[i])//i没被访问过了 { tarjan(i); } } for(int i=1;i<=cnt;i++) { if(num[i]>1) { ans++; } } printf("%d\n",ans); return 0; } void tarjan(int u) { int v; vis_num++; dfn[u]=low[u]=vis_num; stack[++top]=u;//入栈 instack[u]=1;//入栈 for(int i=head[u];i;i=edge[i].nex) { v=edge[i].to;//下一个访问的点 if(!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]);//判断u是否为v的子节点 } else if(instack[v]) { low[u]=min(low[u],dfn[v]); } } if(dfn[u]==low[u])//u为强连通分量的根 { cnt++;//强连通分量的个数+1 do//退栈 { num[cnt]++; v=stack[top--]; belong[v]=cnt; instack[v]=0; }while(u!=v); } }
这里的belong数组似乎没有什么用,但很多tarjan题目里需要用到belong,这里讲解tarjan代码就放了上来。
注释里有说的,编号i所属的强连通分量编号,但对于此题没有用
belong[100010];//i从属的强联通分量的序号