介紹一個小科技,三元環計數,利用復雜度分析證明暴力求解是科學的。
具體問題就是,給定一張 $n$ 個點,$m$ 條邊的簡單無向圖,求解無序三元組 $(i,j,k)$ 的數量,其中滿足存在邊 $(i,j), (j,k),(i,k)$。
我們先把無向圖轉成有向圖,並給每個點定義一個雙關鍵字$(deg_{i}, id_{i})$,其中$deg$表示度數,$id$表示標號,這樣對於每一對點都能嚴格比較出大小。
我們把每一條邊重定向成從度數大的點連向度數小的點,我們就可以得到一張有向無環圖。
具體怎么找環:
- 枚舉一個點$i$,將所有$i$點連出的點標記為$i$。
- 枚舉一個$i$連出的點$j$。
- 枚舉一個$j$連出的點$k$,如果$k$的標記是$i$,那么就找到了一組三元環$(i,j,k)$。
分析每一個三元環只會在$i$這個點被算到一次答案。
下面簡要證明這么做的復雜度的一個上界是$O(m \sqrt{m})$。
考慮重定向后的每條邊$(u,v)$,它對復雜度造成的貢獻就是$out_{v}$,其中$out$表示每個點的出度。
復雜度就是$\sum\limits_{i=1}^{m} out_{v}$。
- 對於所有的$v$,$out_{v} \leq \sqrt{m}$,每一次枚舉$v$連出的點不會超過$O(\sqrt{m})$,$m$次的總復雜度不會超過$O(m\sqrt{m})$。
- 對於所有的$v$,$out_{v} \geq \sqrt{m}$,必然有$deg_{u} \geq deg_{v} \geq \sqrt{m}$,由於$2m = \sum\limits_{i=1}^{n}deg_{i}$,所有這樣的$u$不會超過$\sqrt{m}$個,也就是每一個$out_{v}$的貢獻做多是$\sqrt{m} out_{v}$,由於$\sum\limits_{v}out_{v} \leq m$,所以總復雜度不會超過$O(m \sqrt{m})$。
這樣算三元環的復雜度就科學啦。

#include <cstdio> #include <vector> #include <algorithm> using namespace std; const int N = 250005; int n, m, ans; int a[N], du[N], vi[N], eu[N], ev[N]; vector<int> g[N]; inline bool cmp(int x, int y) { return du[x] != du[y]? du[x] > du[y] : x < y; } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++i) scanf("%d", &a[i]); for (int i = 1; i <= m; ++i) scanf("%d%d", &eu[i], &ev[i]), ++du[eu[i]], ++du[ev[i]]; for (int i = 1; i <= m; ++i) if (cmp(eu[i], ev[i])) g[eu[i]].push_back(ev[i]); else g[ev[i]].push_back(eu[i]); for (int i = 1; i <= n; ++i) { for (int j : g[i]) vi[j] = i; for (int j : g[i]) for (int k : g[j]) if (vi[k] == i) ++ans; } printf("%d\n", ans); return 0; }