给定一个 n 个点 m 条边的简单无向图,求其三元环个数。 n<=1e5,m<=2e5。
Solution
枚举三个点O(n3 ),枚举临边O(m2 ),枚举点及其对边O(nm)都会T,根本原因在于任何一个简单三元环都会被枚举多次。于是我们可以得到一个直观的优化思路:避免对环的重复枚举。 如果是有向图,对于一类子图的计数是不会出现重复统计,因此可以可以按照一个统一的规则将无向图转换成有向图? 方法:对于一条边的两个端点,度小的连向度大的,度数相同的情况下编号小的连向编号大的,这样包含三个点(u,v,w)的三元环就转换成了<u,v>,<v,w>,<u,w>的子图,且关系是一一对应的,在此基础上统计该子图数量即可。 时间复杂度是O(m√m),洛谷题解 上有严格证明 上述分析、优化思路在dp与搜索中具有一定的普适性。
Code
# include <iostream>
# include <cstdio>
# include <vector>
# include <algorithm>
using namespace std;
const int maxn= 1e5 + 5 ;
int lef[ maxn<< 1 ] , rig[ maxn<< 1 ] , n, m, dig[ maxn] , dfn[ maxn] , ans;
vector< int > g[ maxn] ;
int main ( )
{
cin>> n>> m;
for ( int i= 1 , u, v; i<= m; i++ )
{
scanf ( "%d%d" , & u, & v) ;
dig[ u] ++ ; dig[ v] ++ ;
lef[ i] = u; rig[ i] = v;
}
for ( int i= 1 , u, v; i<= m; i++ )
{
if ( dig[ lef[ i] ] == dig[ rig[ i] ] )
{
u= min ( lef[ i] , rig[ i] ) ;
v= max ( lef[ i] , rig[ i] ) ;
}
else {
u= dig[ lef[ i] ] > dig[ rig[ i] ] ? rig[ i] : lef[ i] ;
v= lef[ i] + rig[ i] - u;
}
g[ u] . push_back ( v) ;
}
for ( int i= 1 ; i<= n; i++ )
{
for ( int j= 0 ; j< g[ i] . size ( ) ; j++ )
dfn[ g[ i] [ j] ] = i;
for ( int j= 0 ; j< g[ i] . size ( ) ; j++ )
for ( int k= 0 ; k< g[ g[ i] [ j] ] . size ( ) ; k++ )
if ( dfn[ g[ g[ i] [ j] ] [ k] ] == i) ans++ ;
}
cout<< ans;
return 0 ;
}