【科技】三元环计数
介绍一个小科技,三元环计数,利用复杂度分析证明暴力求解是科学的。
具体问题就是,给定一张 $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; }