有个很妙的算法,可以在 O(mm)O(m \sqrt m) 的时间复杂度内统计无向图的三元环个数。

考虑给每个点赋权,然后令每条边定向,从权值小的连向权值大的,权值相同的,从编号小的连向编号大的,这样可以保证生成的新图是个 DAG。这样每个在原图中的三元环都是长这样的:

我们枚举 uu,再枚举 uu 的出点 vv,最后枚举 vv 的出点 ww,看 ww 是不是也是 uu 的出点,如果是的话则答案加一,它的时间复杂度为每个点的入度与出度之积的和。

然后我们考虑把点权赋为其在原图上的度,这样时间复杂度就是 O(mm)O(m \sqrt m)

具体证明,考虑两种点:入度 <m< \sqrt m 的以及入度 m\ge \sqrt m 的。

对于入度 <m< \sqrt m 的,这样的点的出度总和 m\le m,总贡献为 O(mm)O(m \sqrt m)

对于入度 m\ge \sqrt m 的,这类点至多只有 m\sqrt m 个,所以每个点的出度都 m\le \sqrt m,这样的点入度总和为 mm,总贡献也为 O(mm)O(m \sqrt m)

所以,时间复杂度为 O(mm)O(m \sqrt m)

模板题代码:

#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std;

const int N = 100001, M = 200001;bool vis[N];
int n, m, d[N], u[M], v[M], ans;vector<int> to[N];
int main(){
	scanf("%d%d", &n, &m);
	for (int i = 1;i <= m;i ++) scanf("%d%d", &u[i], &v[i]), d[u[i]] ++, d[v[i]] ++;
	for (int i = 1;i <= m;i ++){
		if (d[u[i]] > d[v[i]] || (d[u[i]] == d[v[i]] && u[i] > v[i])) swap(u[i], v[i]);
		to[u[i]].push_back(v[i]);
	}
	for (int u = 1;u <= n;u ++){
		for (int v : to[u]) vis[v] = 1;
		for (int v : to[u]) for (int x : to[v]) ans += vis[x];
		for (int v : to[u]) vis[v] = 0;
	}printf("%d", ans);
}