• bzoj2208


    首先有向图的题目不难想到先tarjan缩点

    一个强连通分量中的点的连通数显然是相等;

    据说这样直接dfs就可以过了,但显然不够精益求精

    万一给定的是一个完全的DAG图怎么办,dfs铁定超时;

    首先想,dfs进行了很多不必要的操作,比如说i--->j

    那么j的连通数一定也是i的连通数,但我们做dfs是需要做两遍的,降低了效率

    那么为了提高效率,我们希望支持一个这样的操作

    记录下每个点所能到的点,并且能快速的合并;

    不由的想到位运算,但是最多只有30位,而实际有2000个点怎么办?

    那我们就维护最多70个数,每个数表示到达情况

    dp[k,i]为一个表示连通状况的数

    第i个数的二进制上第j个位置(位置从右往左,0~30) 代表点k能否到达点(i-1)*30+j+1 (1代表可到达,0代表不可)

    然后从出度为0的点不断dp即可;

    具体见程序,表达不清,时间复杂度大约是O(nm/30) 还是非常优秀的

      1 type link=^node;
      2      node=record
      3        po:longint;
      4        next:link;
      5      end;
      6 
      7 var edge,way:array[0..2010] of link;
      8     v,f:array[0..2010] of boolean;
      9     be,count,dfn,low,st:array[0..2010] of longint;
     10     dp:array[0..2200,0..70] of longint;
     11     ans,s,state,h,t,l,x,y,i,j,n,m:longint;
     12     ch:ansistring;
     13     p:link;
     14 
     15 function min(a,b:longint):longint;
     16   begin
     17     if a>b then exit(b) else exit(a);
     18   end;
     19 
     20 procedure add(y:longint;var q:link);
     21   var p:link;
     22   begin
     23     new(p);
     24     p^.po:=y;
     25     p^.next:=q;
     26     q:=p;
     27   end;
     28 
     29 procedure tarjan(x:longint);
     30   var y:longint;
     31       p:link;
     32   begin
     33     p:=edge[x];
     34     v[x]:=true;
     35     f[x]:=true;
     36     inc(h);
     37     dfn[x]:=h;
     38     low[x]:=h;
     39     inc(t);
     40     st[t]:=x;
     41     while p<>nil do
     42     begin
     43       y:=p^.po;
     44       if not v[y] then
     45       begin
     46         tarjan(y);
     47         low[x]:=min(low[x],low[y]);
     48       end
     49       else if f[y] then
     50         low[x]:=min(low[x],low[y]);
     51       p:=p^.next;
     52     end;
     53     if low[x]=dfn[x] then
     54     begin
     55       inc(s);
     56       while st[t+1]<>x do
     57       begin
     58         y:=st[t];
     59         f[y]:=false;
     60         be[y]:=s;
     61         inc(count[s]);
     62         dec(t);
     63       end;
     64     end;
     65   end;
     66 
     67 procedure merge(x,y:longint);   //合并点的连通情况
     68   var i,j:longint;
     69   begin
     70     for i:=1 to state do
     71       dp[x,i]:=dp[x,i] or dp[y,i];
     72   end;
     73 
     74 function get(x:longint):longint;  
     75   var i,j,r:longint;
     76   begin
     77     get:=0;
     78     for i:=1 to state do  //穷举每个数
     79       for j:=0 to 29 do   //穷举二进制的每一位
     80       begin
     81         r:=1 shl j;    //位运算的技巧
     82         if r>dp[x,i] then break;  
     83         if (dp[x,i] and r)<>0 then
     84           get:=get+count[(i-1)*30+j+1]; 
     85       end;
     86   end;
     87 
     88 begin
     89   readln(n);
     90   for i:=1 to n do
     91   begin
     92     readln(ch);
     93     for j:=1 to n do
     94     begin
     95       x:=ord(ch[j])-48;
     96       if x<>0 then add(j,edge[i]);
     97     end;
     98   end;
     99   for i:=1 to n do
    100     if not v[i] then
    101     begin
    102       h:=0;
    103       t:=0;
    104       tarjan(i);
    105     end;
    106 
    107   fillchar(dfn,sizeof(dfn),0);
    108   for i:=1 to n do
    109   begin
    110     p:=edge[i];
    111     while p<>nil do
    112     begin
    113       y:=p^.po;
    114       if be[y]<>be[i] then
    115       begin
    116         inc(dfn[be[i]]);      //计算出度
    117         add(be[i],way[be[y]]);   //缩点后记录点be[y]被那些点指向
    118       end;
    119       p:=p^.next;
    120     end;
    121   end;
    122   fillchar(v,sizeof(v),false);
    123   t:=0;
    124   for i:=1 to s do
    125     edge[i]:=nil;
    126   for i:=1 to s do
    127   begin
    128     p:=way[i];
    129     while p<>nil do
    130     begin
    131       x:=p^.po;
    132       add(i,edge[x]);   //记录点i指向那些点
    133       p:=p^.next;
    134     end;
    135     x:=(i-1) div 30+1;
    136     y:=(i-1) mod 30;
    137     dp[i,x]:=1 shl y;   //每个点对自己都是可达的
    138   end;
    139   for i:=1 to s do
    140     if dfn[i]=0 then   //从出度为0的点开始dp
    141     begin
    142       inc(t);
    143       st[t]:=i;
    144     end;
    145 
    146   state:=s div 30+1;
    147   l:=0;
    148   while l<=t do
    149   begin
    150     inc(l);
    151     x:=st[l];
    152     p:=edge[x];
    153     while p<>nil do
    154     begin
    155       y:=p^.po;
    156       merge(x,y);   //每个x指向的点的连通数一定也是x的连通数,合并
    157       p:=p^.next;
    158     end;
    159     ans:=ans+count[x]*get(x);  //计算连通数
    160     p:=way[x];
    161     while p<>nil do     //类似拓扑排序,删除点x,寻找新的出度为0的点
    162     begin
    163       y:=p^.po;
    164       dec(dfn[y]);
    165       if dfn[y]=0 then
    166       begin
    167         inc(t);
    168         st[t]:=y;
    169       end;
    170       p:=p^.next;
    171     end;
    172   end;
    173   writeln(ans);
    174 end.
    View Code
  • 相关阅读:
    最小树形图 朱刘算法模板+建边技巧
    模板倍增LCA 求树上两点距离 hdu2586
    【瞎搞题】gym226123 L. For the Honest Election
    【凸包板题】Gym
    集合3
    集合2
    集合1
    常用API&异常
    内部类&API
    多态&接口类&接口
  • 原文地址:https://www.cnblogs.com/phile/p/4473197.html
Copyright © 2020-2023  润新知