bzoj 3887 tarjan+拓扑排序

11 篇文章 0 订阅

题意:给定一张有向图,从1开始随便走最后回到1,有一次机会可以反向沿着某条边走一次,求最多能经过多少个点,(每个点最多对答案贡献一次)

如果不能反向走,那么答案就是1所在的强连通分量的大小

当我们反了一条边后,答案就是反边后含1所在强连通分量的环

如果我们断开我们所反的边和1,那么会形成两部分——从1所在强连通分量走出和回到1所在强连通分量

所以我们先缩点

再利用拓扑排序分别求出缩点后的1所在的点与其他点在正图和反图的最长链,记做f1[x]和f2[x] (x表示在新图里的编号)

我们所要反向的边一定是强连通分量之间的边

枚举我们所要走反向的边l,

假设l所连接的强连通分量是 a -> b

ans=max{f1[b]+f2[a],2*size[belong[1]]}

因为每个点只对答案贡献一次,所以最后要减去1所在连通分量的大小

uses math;
var
        n,m,x,y,l,tt    :longint;
        time,top,ans    :longint;
        i               :longint;
        z,dfn,low       :array[0..100010] of longint;
        belong,size,que :array[0..100010] of longint;
        last            :array[0..100010] of longint;
        pre,other       :array[0..100010] of longint;
        vis             :array[0..100010] of boolean;
        la,f,indu       :array[0..2,0..100010] of longint;
        pr,ot           :array[0..2,0..100010] of longint;
        ll              :array[0..2] of longint;

procedure connect(x,y:longint);
begin
   inc(l);
   pre[l]:=last[x];
   last[x]:=l;
   other[l]:=y;
end;

procedure connect2(x,y,op:longint);
begin
   inc(ll[op]);
   pr[op,ll[op]]:=la[op,x];
   la[op,x]:=ll[op];
   ot[op,ll[op]]:=y;
end;

procedure dfs(x:longint);
var
        p,q,cur:longint;
begin
   inc(time);
   low[x]:=time;
   dfn[x]:=time;
   inc(top);
   z[top]:=x;
   vis[x]:=true;
   //
   q:=last[x];
   while q<>0 do
   begin
      p:=other[q];
      if dfn[p]=0 then
      begin
         dfs(p);
         low[x]:=min(low[x],low[p]);
      end else
      if vis[p] then low[x]:=min(low[x],dfn[p]);
      q:=pre[q];
   end;
   //
   if low[x]=dfn[x] then
   begin
      cur:=-1; inc(tt);
      while cur<>x do
      begin
         cur:=z[top];
         dec(top);
         vis[cur]:=false;
         belong[cur]:=tt;
         inc(size[tt]);
      end;
   end;
end;

procedure rebuild;
var
        i,p,q:longint;
begin
   for i:=1 to n do
   begin
      q:=last[i];
      while q<>0 do
      begin
         p:=other[q];
         if (belong[i]<>belong[p]) then
         begin
            connect2(belong[i],belong[p],1); inc(indu[1,belong[p]]);
            connect2(belong[p],belong[i],2); inc(indu[2,belong[i]]);
         end;
         q:=pre[q];
      end;
   end;
end;

procedure topsort(x:longint);
var
        i,p,q,h,tl,cur:longint;
begin
   f[x,belong[1]]:=size[belong[1]];
   h:=0; tl:=0;
   for i:=1 to tt do if indu[x,i]=0 then
   begin
      inc(tl); que[tl]:=i; indu[x,i]:=-1;
   end;
   //
   while h<=tl do
   begin
      inc(h);
      cur:=que[h];
      q:=la[x,cur];
      while q<>0 do
      begin
         p:=ot[x,q];
         f[x,p]:=max(f[x,p],f[x,cur]+size[p]);
         dec(indu[x,p]);
         if indu[x,p]=0 then
         begin
            inc(tl); que[tl]:=p; indu[x,p]:=-1;
         end;
         q:=pr[x,q];
      end;
   end;
end;

procedure find;
var
        i,p,q:longint;
begin
   for i:=1 to tt do
   begin
      q:=la[1,i];
      while q<>0 do
      begin
         p:=ot[1,q];
         ans:=max(ans,f[1,p]+f[2,i]);
         q:=pr[1,q];
      end;
   end;
end;

begin
   read(n,m);
   for i:=1 to m do
   begin
      read(x,y);
      connect(x,y);
   end;
   for i:=1 to n do if dfn[i]=0 then dfs(i);
   //
   rebuild;
   for i:=1 to tt do f[1,i]:=-maxlongint div 10;
   for i:=1 to tt do f[2,i]:=f[1,i];
   topsort(1);
   topsort(2);
   //
   ans:=2*size[belong[1]];
   find;
   writeln(ans-size[belong[1]]);
end.
——by Eirlys



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值