题目描述
给定一个nn个点的无向图,求这个图中有多少条长度为
4
的简单路径。
n≤
1500
输入
第一行一个数n
接下来n行每行n个
0
或
1
第i行第j列是
1
表示i与j联通
输出
输出简单路径的个数
样例输入
5
00011
00000
00010
10100
10000
样例输出
2
提示
n<=
1500
这道题目的朴素算法可以直接想到—枚举图中的
4
个点,判断是否构成一条简单路径,时间复杂度O(n^
4
)
那么必然要进行优化
如何优化呢?
我们枚举每一条边,统计连向该边左右两端的节点
如图所示
由乘法原理可得,简单路径的数量为n*m
同时我们不能忽略环的存在,所以还要减去环的数量
所以ans=ans+(节点u的入度-
1
)*(节点v的入读-
1
)-三元环的数量
{注意本身枚举的那一条边}
此时时间复杂度为O(n^
3
),
似乎还是A不了诶
此时我们把目光放在了寻找三元环的n上
怎么才能将这个n降下来呢
嗯.......压位!!!
对于每个节点i,我们用f[i]数组来表示它的连接情况
其中里面所存放的是一个二进制数
数中的第k位表示的是点i是否与点k相连
我们发现n<=
1500
,那么就有
1500
个二进制位要存储
所以我们再加一维放入
50
个
longint
(c++是int
32
位)
那么处理三元环就直接变成了n/
30
(对于f数组我们进行预处理)
时间复杂度就变成了(n^
3
/
30
)
n<=
1500
这个时间复杂度是可以接受的
接下来是代码
var n,i,j,k,t,l,dd:longint; ans,sum:int64; f:array[1..3000,1..100] of longint; a:array[1..1600,1..1600] of char; GGG:array[0..1 shl 16] of longint; u,v:array[1..1500*1500] of longint; s:array[1..2000] of longint; function find(q:longint):longint; //对于一个二进制数求解里面有几个1 var left,right:longint; begin left:=q shr 15; right:=q-left shl 15;//因为这个二进制数是30位的,而我们的表只有15位,那么就把这个数掰开 exit(GGG[left]+GGG[right]); end; procedure GG; //打表处理出2~2^16中所有二进制数中的1的数量 begin for i:=2 to 1 shl 16 do begin GGG[i]:=GGG[i div 2]+i mod 2; //递推求解不解释 end; end; begin readln(n); GGG[1]:=1; //GGG[i]表示i的二进制中有几个一 GG; for i:=1 to n do begin for j:=1 to n do begin read(a[i,j]); if (a[i,j]='1') and (i<>j) then begin inc(l); u[l]:=i; v[l]:=j; //记录边两端的点 inc(s[i]);//记录入度 end; end; readln; end; for i:=1 to n do begin for j:=1 to n do begin t:=j div 30+1; if a[i,j]='1' then f[i][t]:=f[i][t]+(1 shl (j-(t-1)*30+1)); end; end; //构建f数组 for k:=1 to l do begin sum:=(s[u[k]]-1)*(s[v[k]]-1); for i:=1 to n div 30+1 do begin dd:=0; dd:=find(f[v[k]][i] and f[u[k]][i]); //and后二进制位上就直接保留了相同的点 sum:=sum-dd; end; ans:=ans+sum; end; writeln(ans); end.