自行车_纪中1238_缩点+拓扑排序+递推

Description

自行车赛在一个很大的地方举行,有N个镇,用1到N编号,镇与镇之间有M条单行道相连,起点设在镇1,终点设在镇2。
问从起点到终点一共有多少种不同的路线。两条路线只要不使用完全相同的道路就被认为是不同的。

Input

第一行两个整数:N和M(1<=N<=10000,1<=M<=100000),表示镇的数量和道路的数量。
接下来M行,每行包含两个不同的整数A和B,表示有一条从镇A到镇B的单行道。
两个镇之间有可能不止一条路连接。

Output

输出不同路线的数量,如果答案超过9位,只需输出最后9位数字。如果有无穷多的路线,输出“inf”。

Sample Input

输入1:

6 7

1 3

1 4

3 2

4 2

5 6

6 5

3 4

输入2:

6 8

1 3

1 4

3 2

4 2

5 6

6 5

3 4

4 3

Sample Output

输出1:

3

输出2:

inf

题解:

可以这样想:

  1. 如果两点之间有无限多种方案数,表示在两点间的必经之路上有一个环
  2. 对于环的处理我们考虑用tarjan算法求强连通分量且缩点建新图G
  3. 对新图G作拓扑排序并按拓扑序作递推
  4. 注意要取后9位且有前导零

递推公式:

f[i]=j=1nf[j]ji

经过多轮调♂试,我们可以发现

这道题并没有inf的数据点!!!!!

于是我们不考虑有inf的情况即可

代码

type
  edge=record
    x,y,next:longint;
  end;
  arr=array[1..100000]of edge;
const
  p=1000000000;
var
  n,m,t,maxE,maxG:longint;
  comp,ind,low,dfn,num,ls,s:array[0..10000]of longint;
  state:array[0..1000000]of longint;
  v:array[1..10000]of boolean;
  flag:boolean;
  e,g:arr;
function min(x,y:longint):longint;
begin
  min:=x;
  if y<x then
  min:=y;
end;
procedure add(var q:arr;var max,x,y:longint);
begin
  inc(max);
  q[max].x:=x;
  q[max].y:=y;
  q[max].next:=ls[x];
  ls[x]:=max;
end;
procedure tarjan(x:longint);
var
  i,y:longint;
begin
  inc(t);
  dfn[x]:=t;
  low[x]:=t;
  inc(s[0]);
  s[s[0]]:=x;
  v[x]:=true;
  i:=ls[x];
  while i>0 do
  begin
    y:=e[i].y;
    if dfn[y]=0 then
    begin
      tarjan(y);
      low[x]:=min(low[y],low[x]);
    end
    else
    if v[y] then
    low[x]:=min(low[x],dfn[y]);
    i:=e[i].next;
  end;
  if dfn[x]=low[x] then
  begin
    inc(comp[0]);
    repeat
      y:=s[s[0]];
      comp[y]:=comp[0];
      v[y]:=false;
      dec(s[0]);
    until x=y;
  end;
end;
procedure topsort;
var
  i,head,tail:longint;
begin
  fillchar(ind,sizeof(ind),0);
  for i:=1 to maxE do
  inc(ind[e[i].y]);
  head:=0;
  tail:=0;
  for i:=1 to comp[0] do
  if ind[i]=0 then
  begin
    inc(tail);
    state[tail]:=i;
  end;
  num[comp[1]]:=1;
  repeat
    inc(head);
    i:=ls[state[head]];
    while i>0 do
    with e[i] do
    begin
      dec(ind[y]);
      num[y]:=num[y]+num[x];
      if num[y]>=p then
      begin
        flag:=true;
        num[y]:=num[y]mod p;
      end;
      if ind[y]=0 then
      begin
        inc(tail);
        state[tail]:=y;
      end;
      i:=next;
    end;
  until head>=tail;
  t:=tail;
end;
procedure print;
var
  i:longint;
  st:string;
begin
  str(num[comp[2]],st);
  if flag then
  for i:=1 to 9-length(st) do write(0);
  writeln(st);
end;
procedure main;
var
  i:longint;
begin
  for i:=1 to n do
  if dfn[i]=0 then
  tarjan(i);
  fillchar(ls,sizeof(ls),0);
  for i:=1 to maxE do
  if comp[e[i].x]<>comp[e[i].y] then
  add(g,maxG,comp[e[i].x],comp[e[i].y]);
  maxE:=maxG;
  e:=g;
  topsort;
end;
procedure init;
var
  i,x,y:longint;
begin
  readln(n,m);
  for i:=1 to m do
  begin
    readln(x,y);
    add(e,maxE,x,y);
  end;
end;
begin
  init;
  main;
  print;
end.

转载于:https://www.cnblogs.com/olahiuj/p/5781264.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值