首先有向图的题目不难想到先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

 

posted on 2014-07-19 15:42  acphile  阅读(197)  评论(0编辑  收藏  举报