poj2186

题目链接

http://poj.org/problem?id=2186

题目大意

N个点 (1 <= N <= 10,000), M条边 (1 <= M <= 50,000),(A B)表示点A指向点B,输出有多少个点是任意点可以指向的

样例

输入

3 3
1 2
2 1
2 3

输出

1

思路

强连通分量
邻接表
答案需考虑特例:缩点后依旧存在多个强连通分量
这题数据范围m给小了应该是m<=500000

法一

1.时间戳
2.进栈
3.深搜
4.回溯
4.1.low修改
4.2时间戳=父节点时间戳时弹栈到到当前节点

法二

1.正向建边深搜,退栈时打时间戳,使得时间戳最大的点是没有入度的点
2.反向建边,从时间戳大的点开始深搜,搜索到的点是一个强连通分量

法一弱智代码

#include<cstdio>
#include<cstring>
const int nn=10005;
const int mm=500005;
struct rec{int a,b,h;}original[mm];
int n,m,h[nn],a,b,low[nn],dfn[nn],flag[nn],time,sl,st[nn];
int num,sum[nn],now[nn],ans,send[nn],f;
int min(int x,int y){return x<y ?x:y;}
int dfs(int k){
	dfn[k]=low[k]=++time;
	st[++sl]=k; flag[k]=1;
	for(int x_h=h[k]; x_h!=-1; x_h=original[x_h].h){
		int y=original[x_h].b;
		if(flag[y]!=0)low[k]=min(low[k],low[y]);
		if(dfn[y]==0)low[k]=min(low[k],dfs(y));
	}
	if(dfn[k]==low[k]){
			++num;
			int summ=0;
			do{
				++summ;
				flag[st[sl]]=0;
				now[st[sl--]]=num;
			}while(st[sl+1]!=k);
			sum[num]=summ;
	}
	return(low[k]);
}
int main(){
	//freopen("poj2186.out","w",stdout);
	scanf("%d%d",&n,&m);
	memset(h,-1,sizeof(h));
	for(int i=1; i<=m; ++i){
		scanf("%d%d",&a,&b);
		original[i].a=a;
		original[i].b=b;
		original[i].h=h[a];
		h[a]=i;
	}
	for(int i=1; i<=n; ++i)low[i]=nn;
	time=0; sl=0; 
	memset(dfn,0,sizeof(dfn));
	memset(flag,0,sizeof(flag));
	for(int i=1; i<=n; ++i)if(dfn[i]==0)dfs(i);
	memset(send,0,sizeof(send));
	for(int i=1; i<=m; ++i)
		if(now[original[i].a]!=now[original[i].b])
			++send[now[original[i].a]];
	ans=0;
	f=0;
	for(int i=1; i<=num; ++i){
		if(send[i]==0){
			if(f==1){
				ans=0;
				break;
			}else{
				ans=sum[i];
				f=1;
			}
		}
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2018-08-10 01:36  秦宝宝&范可爱  阅读(86)  评论(0)    收藏  举报