bzoj 2208 缩点+dfs

11 篇文章 0 订阅
7 篇文章 0 订阅

题目:

乱搞复杂度O(n^2)..

下面是黑字是蒟蒻的吐槽,蓝字是蒟蒻的口胡,仅供娱乐

其实一开始是想用记忆化搜索的..然后发现如果出现环的话,会访问到已经访问过的点且这个点的答案并没有完成更新,就会出错,所以就先用tarjan缩点重建图,这样就可以形成一个有向无环图,就避免了刚擦出错的情况

然后对于缩点后的图依旧是采用记忆化搜索去找它的连通数,对于一个已经找过联通数的点就不再去找直接累加。然后又会发现,还是太naive了,因为这样会重复,如题目中的图,2会直接加点3的答案即加3,同时2又会加上点5的答案,所以更新出来的点2除了它本身看到了4个点...想了想并没有什么好办法来判断重复,一开始又加了个vis,如果访问过就不再用它更新答案,但是又会遇到新的问题,即一个点被其他点访问过但同时又是这个点的答案,就更新不到它,答案就会少.....所以干脆就去掉了记忆化,但是并没有什么用,2依旧还会看到4个点...

最后,便开启了无脑暴搜模式,对每一个点做dfs,仅对当前这次dfs中访问过的点打上标记避免重复,每次找完一个点恢复所有点的标记以免查下一个点的时候更新不到答案

对于每个缩点后的点,它对其他点的贡献为它的size,它自己对答案的贡献为 size^2 + tt*size (其中tt表示它能到达的点的个数)

var
        n,l,time,top,tt :longint;
        ans             :int64;
        pre,other       :array[0..8000010] of longint;
        last,size       :array[0..4010] of longint;
        dfn,low,belong,z:array[0..2010] of longint;
        vis,vis2        :array[0..4010] of boolean;
        i,j             :longint;
        s               :ansistring;
function min(a,b:longint):longint;
begin
   if a<b then exit(a) else exit(b);
end;

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

procedure dfs(x:longint);
var
        p,q,cur:longint;
begin
   inc(time);
   dfn[x]:=time;
   low[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;
      while cur<>x do
      begin
         cur:=z[top];
         dec(top);
         vis[cur]:=false;
         belong[cur]:=n+x;
         inc(size[n+x]);
      end;
   end;
end;

procedure dfs2(x:longint);
var
        p,q:longint;
begin
   vis2[x]:=true;
   q:=last[x];
   while (q<>0) do
   begin
      p:=other[q];
      if not vis2[p] then
      begin
         inc(tt,size[p]);
         dfs2(p);
      end;
      q:=pre[q];
   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[p]<>belong[i] then connect(belong[i],belong[p]);
         q:=pre[q];
      end;
   end;
end;

begin
   readln(n);
   for i:=1 to n do
   begin
      readln(s);
      for j:=1 to n do if s[j]='1' then connect(i,j);
   end;
   for i:=1 to n do if dfn[i]=0 then dfs(i);
   rebuild;
   for i:=1 to n do if not vis[belong[i]] then
   begin
      vis[belong[i]]:=true; tt:=0;
      dfs2(belong[i]);
      for j:=1 to n do vis2[belong[j]]:=false;
      inc(ans,int64(size[belong[i]])*int64(size[belong[i]]));
      inc(ans,int64(size[belong[i]])*int64(tt));
   end;
   writeln(ans);
end.
——by Eirlys


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值