这不是floyd

tarjan缩点,同一个强联通分量中,元素只有一个就不需要连边,否则就连成一个环,ans+=元素个数

处理强联通分量直接连边有点麻烦,先把所有的边都连上,并记录连边的出发点和终点(l[num],r[num]),ans+=num

用p[i][j]来记录强联通分量i到j的走法总数,用floyd计算,p[i][j]+=p[i][k]*p[k][j] (>0),然后再枚举每一条连着的边,看能否删掉,能删掉的标志是p[i][j]>1,于是ans--,p[i][j]--

附代码:

var n,m,o,i,ii,j,k,v,x,y,tot,top,co,num,ans:longint;
head,next,vet:array[0..40005] of longint;
color,st,count,dfn,low,d,l,r:array[0..300] of longint;
map,p:array[0..205,0..205] of longint;
b:array[0..205] of boolean;

function min(a,b:longint):longint;
begin if A>b then exit(b);exit(a);end;

procedure tarjan(x:longint);
var i,v:longint;
begin
inc(num);low[x]:=num;dfn[x]:=num;
inc(top);st[top]:=x;
i:=head[x];
while i<>0 do begin
    v:=vet[i];
    if dfn[v]=0 then begin
        tarjan(v);
        low[x]:=min(low[x],low[v]);
        end
    else if color[v]=0 then low[x]:=min(low[x],dfn[v]);
    i:=next[i];
    end;
if dfn[x]=low[x] then begin
    inc(co);
    while st[top]<>x do begin
        color[st[top]]:=co;
        inc(count[co]);
        dec(top);
        end;
        inc(count[co]);
        color[st[top]]:=co;
        dec(top);
    end;
end;

begin
readln(o);
for o:=o downto 1 do begin
readln(n);
tot:=0;num:=0;co:=0;top:=0;ans:=0;
for i:=1 to n do begin b[i]:=false;dfn[i]:=0;low[i]:=0;count[i]:=0;color[i]:=0;head[i]:=0;d[i]:=0;end;
for i:=1 to n do
    for j:=1 to n do begin
        read(x);map[i,j]:=x;p[i,j]:=0;
        if i=j then continue;
        if x=1 then begin
            inc(tot);
            next[tot]:=head[i];
            head[i]:=tot;
            vet[tot]:=j;
            end;
        end;
for i:=1 to n do if dfn[i]=0 then tarjan(i);
for i:=1 to n do begin
    ii:=head[i];
    while ii<>0 do begin
        v:=vet[ii];
        if color[v]<>color[i] then
                inc(d[color[v]]);
        ii:=next[ii];
        end;
    end;
num:=0;
for i:=1 to n do
    for j:=1 to n do
        if (map[i][j]=1)and(color[i]<>color[j]) then  begin
        inc(num);
        l[num]:=color[i];
        r[num]:=color[j];
        inc(p[color[i]][color[j]]);
        end;
for k:=1 to co do
    for i:=1 to co do
        for j:=1 to co do
            if (p[i,k]>0)and(p[k][j]>0) then inc(p[i,j],p[i,k]*p[k][j]);
ans:=ans+num; 
for i:=1 to num do
    if p[l[i]][r[i]]=1 then continue else begin dec(ans);dec(p[l[i]][r[i]]);end;
for i:=1 to co do
    if count[i]>1 then ans:=ans+count[i];
writeln(ans);
end;
end.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值