题解 P1989 无向图三元环计数

Miko35

2021-04-20 16:31:36

题解

bitset 好写,来写 bitset!!1

首先有一个非常显然的暴力,就是枚举每条边的两个端点 u,v,若存在一个点 w 满足 u,v 都对 w 有连边,那 u,v,w 就是一个合法三元组,时间复杂度 O(nm)

考虑下 bitset 优化。维护一个 bitset S,其中 S_i 表示与 i 有连边的点集。在上述暴力中,枚举 u,v 两个点的复杂度为 O(m),枚举 w 的过程,其实就是求 S_uS_v 的交集,用 & 实现可以做到 O\left(\dfrac{n}{w}\right),时间复杂度为 O\left(\dfrac{nm}{w}\right)

然后你一写就会发现,这个邻接矩阵空间复杂度为 O(n^2)

如果 bitset 真的死了,那你就不会看见这篇题解了。

邻接矩阵比起邻接表,空间劣在它存储了很多无用的 0。如何去根据这玩意改进?

有个典中典叫 bitset 做高维偏序,空间优化是分块,这里是根号分治。

那就设置一个阈值 $B$,度数 $>B$ 的点给它开完整 bitset,这种点不会超过 $\min\left(n,\dfrac{m}{B}\right)$ 个;度数 $\leq B$ 的点就枚举连边,暴力判断。 时间复杂度?考虑计算每个点的贡献。所有度数 $>B$ 的点最多会被 $O(m)$ 条边枚举到,一次计算是 $O\left(\dfrac{n}{w}\right)$,所以这一部分复杂度 $O\left(\dfrac{nm}{w}\right)$。一个度数 $\leq B$ 的点 $u$ 的点的贡献为 $d_u^2$,其中 $d_u$ 表示 $u$ 的度数。那就有 $\sum d_u\leq m,d_u\leq B$,总共 $\sum d_u^2\leq B\cdot m$。 时间复杂度 $O\left(\dfrac{nm}{w}+B\cdot m\right)$,空间复杂度 $O\left(\dfrac{nm}{B}\right)$。这道题不卡空间,$B$ 取小一点能过,稳妥起见还是取 $O(\sqrt m)$。 bitset 优点在于好背好写,要是考场突然忘了三元环计数,bitset 也是不错的选择。 ```cpp #include<bits/stdc++.h> #define rgi register int using namespace std; const int N=100010,B=660; int n,m,u,v,d[N],bel[N],cnt,ans; bitset<N>g[700],s; vector<int>a[N]; signed main(){ scanf("%d%d",&n,&m); for(rgi i=1;i<=m;++i){ scanf("%d%d",&u,&v),++d[u],++d[v]; a[u].push_back(v),a[v].push_back(u); } for(rgi i=1;i<=n;++i){ s.reset(); for(rgi j:a[i])s[j]=1; if(d[i]>=B)g[bel[i]=++cnt]=s; for(rgi j:a[i]){ if(j<i){ if(bel[j])ans+=(g[bel[j]]&s).count(); else for(rgi k:a[j])ans+=s[k]; } } } printf("%d",ans/3); return 0; } ```