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_u 与 S_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;
}
```