【科技】三元环计数

介绍一个小科技,三元环计数,利用复杂度分析证明暴力求解是科学的。

 具体问题就是,给定一张 $n$ 个点,$m$ 条边的简单无向图,求解无序三元组 $(i,j,k)$ 的数量,其中满足存在边 $(i,j), (j,k),(i,k)$。

 我们先把无向图转成有向图,并给每个点定义一个双关键字$(deg_{i}, id_{i})$,其中$deg$表示度数,$id$表示标号,这样对于每一对点都能严格比较出大小。

我们把每一条边重定向成从度数大的点连向度数小的点,我们就可以得到一张有向无环图。

具体怎么找环:

  1. 枚举一个点$i$,将所有$i$点连出的点标记为$i$。
  2. 枚举一个$i$连出的点$j$。
  3. 枚举一个$j$连出的点$k$,如果$k$的标记是$i$,那么就找到了一组三元环$(i,j,k)$。

 分析每一个三元环只会在$i$这个点被算到一次答案。

下面简要证明这么做的复杂度的一个上界是$O(m \sqrt{m})$。

 考虑重定向后的每条边$(u,v)$,它对复杂度造成的贡献就是$out_{v}$,其中$out$表示每个点的出度。

复杂度就是$\sum\limits_{i=1}^{m} out_{v}$。

  1. 对于所有的$v$,$out_{v} \leq \sqrt{m}$,每一次枚举$v$连出的点不会超过$O(\sqrt{m})$,$m$次的总复杂度不会超过$O(m\sqrt{m})$。
  2. 对于所有的$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;
}
View Code

 

posted @ 2018-10-09 13:30  Dance_Of_Faith  阅读(1622)  评论(0编辑  收藏