Tarjan的教程网上都有
http://www.cnblogs.com/shadowland/p/5872257.html
【COGS8】 备用交换机
【问题描述】
n个城市之间有通讯网络,每个城市都有通讯交换机,直接或间接与其它城市连接。因电子设备容易损坏,需给通讯点配备备用交换机。但备用交换机数量有限,不能全部配备,只能给部分重要城市配置。于是规定:如果某个城市由于交换机损坏,不仅本城市通讯中断,还造成其它城市通讯中断,则配备备用交换机。请你根据城市线路情况,计算需配备备用交换机的城市个数,及需配备备用交换机城市的编号。
【输入格式】
输入文件有若干行
第一行,一个整数n,表示共有n个城市(2<=n<=100)
下面有若干行,每行2个数a、b,a、b是城市编号,表示a与b之间有直接通讯线路。
【输出格式】
输出文件有若干行
第一行,1个整数m,表示需m个备用交换机,下面有m行,每行有一个整数,表示需配备交换机的城市编号,输出顺序按编号由小到大。如果没有城市需配备备用交换机则输出0。
【输入输出样例】
输入文件名: gd.in
7
1 2
2 3
2 4
3 4
4 5
4 6
4 7
5 6
6 7
输出文件名:gd.out
2
2
4
求割点
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct edge
{
int v, next;
}map[10001];
int n, a, b, root, cnt=0, dep=0, ans=0;
int head[101], low[101], dfn[101];
bool point[101];
void add(int x, int y)
{
map[++cnt].v = y;
map[cnt].next = head[x];
head[x] = cnt;
}
void tarjan(int x)
{
int son = 0;
low[x] = dfn[x] = ++dep;
for(int i=head[x]; i!=0; i=map[i].next)
{
if(dfn[map[i].v]==0)
{
tarjan(map[i].v); son++;
low[x] = min(low[x], low[map[i].v]);
if((x==root && son>1) || (x!=root && low[map[i].v]>=dfn[x]))
if(!point[x]) point[x] = true, ans++;
}
else low[x] = min(low[x], dfn[map[i].v]);
}
}
int main()
{
freopen("gd.in", "r", stdin);
freopen("gd.out", "w", stdout);
scanf("%d", &n);
while(scanf("%d%d", &a, &b)!=EOF) {add(a, b);add(b, a);}
for(int i=1; i<=n; i++)
if(dfn[i]==0) {root=i; tarjan(i);}
printf("%d\n", ans);
for(int i=1; i<=n; i++)
{
if(point[i]) printf("%d\n", i);
}
return 0;
}
【COGS 618】 传话
[问题描述]
兴趣小组的同学来自各个学校,为了增加友谊,晚会上又进行了一个传话游戏,如果 a 认识 b ,那么 a 收到某个消息,就会把这个消息传给 b ,以及所有 a 认识的人。
如果 a 认识 b , b 不一定认识 a 。
所有人从 1 到 n 编号,给出所有“认识”关系,问如果 i 发布一条新消息,那么会不会经过若干次传话后,这个消息传回给了 i , 1<=i<=n 。
[输入文件]
输入文件 message.in 中的第一行是两个数 n(n<1000) 和 m(m<10000) ,两数之间有一个空格,表示人数和认识关系数。
接下来的 m 行,每行两个数 a 和 b ,表示 a 认识 b 。 1<=a, b<=n 。认识关系可能会重复给出,但一行的两个数不会相同。
[输出文件]
输出文件 message.out 中一共有 n 行,每行一个字符 T 或 F 。第 i 行如果是 T ,表示 i 发出一条新消息会传回给 i ;如果是 F ,表示 i 发出一条新消息不会传回给 i 。
[输入样例]
4 6
1 2
2 3
4 1
3 1
1 3
2 3
[输出样例]
T
T
T
F
找强连通分量
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct edge
{
int v, next;
}e[200001];
int u, v, t=0, colnum=0;
int n, m, p=0, cnt=0, dep=0;
int head[100001], dfn[100001], low[100001], sta[100001];
int sum[100001], color[100001];
bool insta[100001];
void add(int x, int y)
{
e[++cnt].next = head[x];
e[cnt].v = y;
head[x] = cnt;
}
void tarjan(int x)
{
dfn[x] = low[x] = ++dep;
sta[++p] = x; insta[x]= true;
for(int i=head[x]; i!=0; i=e[i].next)
{
if(dfn[e[i].v]==0)
{
tarjan(e[i].v);
low[x] = min(low[x], low[e[i].v]);
}
else if(insta[e[i].v]) low[x] = min(low[x], dfn[e[i].v]);
}
if(dfn[x]==low[x])
{
colnum++;
do
{
color[sta[p]] = colnum;
sum[colnum]++;
insta[sta[p]] = false;
}
while(sta[p--]!=x);
}
}
int main()
{
freopen("messagew.in", "r", stdin);
freopen("messagew.out", "w", stdout);
scanf("%d%d", &n, &m);
for(int i=1; i<=m; i++)
{
scanf("%d%d", &u, &v);
add(u, v);// add(v, u);
}
for(int i=1; i<=n; i++) if(dfn[i]==0) tarjan(i);
for(int i=1; i<=n; i++)
{
if(sum[color[i]]>1) printf("T\n");
else printf("F\n");
}
return 0;
}
【COGS 921】 [東方S1] 上白泽慧音
题目描述
在幻想乡,上白泽慧音是以知识渊博闻名的老师。春雪异变导致人间之里的很多道路都被大雪堵塞,使有的学生不能顺利地到达慧音所在的村庄。因此慧音决定换一个能够聚集最多人数的村庄作为新的教学地点。人间之里由N个村庄(编号为1..N)和M条道路组成,道路分为两种一种为单向通行的,一种为双向通行的,分别用1和2来标记。如果存在由村庄A到达村庄B的通路,那么我们认为可以从村庄A到达村庄B,记为(A,B)。当(A,B)和(B,A)同时满足时,我们认为A,B是绝对连通的,记为。绝对连通区域是指一个村庄的集合,在这个集合中任意两个村庄X,Y都满足。现在你的任务是,找出最大的绝对连通区域,并将这个绝对连通区域的村庄按编号依次输出。若存在两个最大的,输出字典序最小的,比如当存在1,3,4和2,5,6这两个最大连通区域时,输出的是1,3,4。
输入格式
第1行:两个正整数N,M
第2..M+1行:每行三个正整数a,b,t, t = 1表示存在从村庄a到b的单向道路,t = 2表示村庄a,b之间存在双向通行的道路。保证每条道路只出现一次。
输出格式
第1行: 1个整数,表示最大的绝对连通区域包含的村庄个数。
第2行:若干个整数,依次输出最大的绝对连通区域所包含的村庄编号。
输入样例
5 5
1 2 1
1 3 2
2 4 2
5 1 2
3 5 1
输出样例
3
1 3 5
数据范围
对于60%的数据:N <= 200且M <= 10,000
对于100%的数据:N <= 5,000且M <= 50,000
强连通分量
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 5001
#define MAXM 50001
int n, m, x, y, t;
int p=0, cnt=0, dep=0, colornum=0;
int dfn[MAXN], low[MAXN], sum[MAXN];
int head[MAXN], stack[MAXN], color[MAXN];
bool instack[MAXN];
struct edge
{
int v, pre;
}e[MAXM];
void add(int u, int v)
{
e[++cnt].v = v;
e[cnt].pre = head[u];
head[u] = cnt;
}
void tarjan(int u)
{
dfn[u] = low[u] = ++dep;
stack[++p] = u; instack[u] = true;
for(int i=head[u]; i!=0; i=e[i].pre)
{
int v = e[i].v;
if(dfn[v]==0)
{
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if(instack[v]) low[u] = min(low[u], dfn[v]);
}
if(low[u]==dfn[u])
{
colornum++;
do
{
color[stack[p]] = colornum;
sum[colornum]++;
instack[stack[p]] = false;
}
while(u!=stack[p--]);
}
}
int main()
{
freopen("classroom.in", "r", stdin);
freopen("classroom.out", "w", stdout);
scanf("%d%d", &n, &m);
for(int i=1; i<=m; i++)
{
scanf("%d%d%d", &x, &y, &t);
add(x, y); if(t==2) add(y, x);
}
for(int i=1; i<=n; i++) if(dfn[i]==0) tarjan(i);
int maxs = 0, outcol = 0;
for(int i=1; i<=n; i++)
{
if(sum[color[i]]>maxs)
{
outcol = color[i];
maxs = sum[color[i]];
}
}
printf("%d\n", maxs);
for(int i=1; i<=n; i++) if(color[i]==outcol) printf("%d ", i);
return 0;
}