2186是我OI史上第一道Tarjan,之前看了很多的题解都是C或者C++的,让我一个学pascal的很难堪,做了一天之后AC,便想到写一篇pascal的解题报告。
题目的大意就是说有若干只奶牛,奶牛之间存在一种单向的羡慕之意,奶牛数为N(1<=n<=10000),羡慕之意有M(1<=M<=50000)条,让我们求出有多少头奶牛是被除自己以外的所有奶牛羡慕。
稍微画一下图就大概明白了要怎么写,其实就是给出一个有向图G,把图G的所有极大强连通分量求出来,然后进行缩点,形成一个无环图C,在这个无环图里,至少有一个是出度为0的点,若出度为0的点不止一个,那么就输出0(因为既然有多个出度为0的点,那么意味着没有一个点被其他所有点到达),否则输出出度为0的那个点是由原来多少个节点缩成。
可以看到题目的数据比较大,用矩阵需要一个10000*10000的数组,必定会爆空间(一开始做的时候用矩阵爆了好几次),用链接表只是50000+10000而已,数组只开10000和50000会WA,所以开大点
代码:
type
rec=record
next,t:longint;
end;
var
f,v:array[1..10010] of boolean;
edge:array[1..50010] of rec;
head,low,dfn,cd,sd,stack:array[1..10010] of longint;
n,m,x,y,ans,deep,top,t,tmp,sum,i:longint;
function min(a,b:integer):longint;
begin
if a<b then exit(a) else exit(b);
end;
procedure pop(k:longint);
begin
while stack[top+1]<>k do
begin
sd[stack[top]]:=ans;
f[stack[top]]:=false;
dec(top);
end;
end;
procedure dfs(k:longint);
var
tmp:longint;
begin
inc(deep);
low[k]:=deep; dfn[k]:=deep; v[k]:=true;
inc(top);
stack[top]:=k; f[k]:=true;
tmp:=head[k];
while tmp<>-1 do
begin
if v[edge[tmp].t]=false then
begin
dfs(edge[tmp].t);
low[k]:=min(low[k],low[edge[tmp].t]);
end
else
if f[edge[tmp].t] then low[k]:=min(low[k],dfn[edge[tmp].t]);
tmp:=edge[tmp].next;
end;
if dfn[k]=low[k] then begin inc(ans); pop(k); end;
end;
begin
readln(n,m);
for i:=1 to n do head[i]:=-1; fillchar(edge,sizeof(edge),0);//初始化
for i:=1 to m do
begin
readln(x,y);
edge[i].t:=y;
edge[i].next:=head[x];
head[x]:=i;
end; //链接表存储
fillchar(v,sizeof(v),false); fillchar(f,sizeof(f),false);
for i:=1 to n do
if v[i]=false then dfs(i);
for i:=1 to n do
begin
t:=i; tmp:=head[i];
while tmp<>-1 do
begin
if sd[t]<>sd[edge[tmp].t] then inc(cd[sd[i]]);
t:=edge[tmp].t; tmp:=edge[tmp].next;
end;
end;
for i:=1 to ans do
if cd[i]=0 then
if sum=1 then begin writeln(0); halt; end
else begin inc(sum); tmp:=i; end; //统计出度为0的连通块
sum:=0;
for i:=1 to n do if sd[i]=tmp then inc(sum);
writeln(sum);
end.