1 条题解

  • 0
    @ 2025-8-24 21:32:41

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 一扶苏一
    休息结束。邮箱 yifusuyi@qq.com

    搬运于2025-08-24 21:32:41,当前版本为作者最后更新于2020-02-08 12:36:05,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    【P1989】【模板】无向图三元环计数

    快来做这个题呀虽然它标号很小但它确实是个刚上传的新题

    Description

    给定一个 nn 个点 mm 条边的简单无向图,求其三元环个数。

    Limitations

    1n1051 \leq n \leq 10^51m2×1051 \leq m \leq 2 \times 10^5

    Solution

    Subtask 1 有三种做法,分别是枚举三个点 O(n3)O(n^3),枚举两条邻边 O(m2)O(m^2),枚举一个点及其对边 O(nm)O(nm)。这里不再赘述。

    我们考虑给所有的边一个方向。具体的,如果一条边两个端点的度数不一样,则由度数较小的点连向度数较大的点,否则由编号较小的点连向编号较大的点。不难发现这样的图是有向无环的。注意到原图中的三元环一定与对应有向图中所有形如 $,,$ 的子图一一对应,我们只需要枚举 uu 的出边,再枚举 vv 的出边,然后检查 ww 是不是 uu 指向的点即可。</p>

    下面证明这个算法的时间复杂度是 O(mm)O(m \sqrt m)

    首先我们可以在枚举 uu 的出边时给其出点打上 uu 的时间戳,这样在枚举 vv 的出边时即可 O(1)O(1) 去判断 ww 是不是 uu 的出点。

    那么考虑对于每一条边 <uv><u \rightarrow v>,它对复杂度造成的贡献是 outvout_v,因此总复杂度即为 i=1moutvi\sum_{i = 1}^m out_{v_i},其中 viv_i 是第 ii 条边指向的点,outvout_v 是点 vv 的出度。

    考虑分情况讨论。

    1. vv 在原图(无向图)上的度数不大于 m\sqrt m 时,由于新图每个节点的出度不可能大于原图的度数,所以 outv=O(m)out_v = O(\sqrt m)
    2. vv 在原图上的度数大于 m\sqrt m 时,注意到它只能向原图中度数不小于它的点连边,又因为原图中所有的点的度数和为 O(m)O(m),所以原图中度数大于 m\sqrt m 的点只有 O(m)O(\sqrt m) 个。因此 vv 的出边只有 O(m)O(\sqrt m) 条,也即 outv=O(m)out_v = O(\sqrt m)

    因此所有节点的出度均为 O(m)O(\sqrt m),总复杂度 i=1moutvi=O(mm)\sum_{i = 1}^m out_{v_i} = O(m \sqrt m)

    Code

    #include <cstdio>
    #include <vector>
    
    const int maxn = 200005;
    
    int n, m, ans;
    int dgr[maxn], A[maxn], B[maxn], vistime[maxn];
    std::vector<int> e[maxn];
    
    int main() {
      qr(n); qr(m);
      for (int i = 1; i <= m; ++i) {
        qr(A[i]); qr(B[i]);
        ++dgr[A[i]]; ++dgr[B[i]];
      }
      for (int u, v; m; --m) {
        u = A[m]; v = B[m];
        if (dgr[u] > dgr[v]) {
          std::swap(u, v);
        } else if ((dgr[u] == dgr[v]) && (u > v)) {
          std::swap(u, v);
        }
        e[u].push_back(v);
      }
      for (int u = 1; u <= n; ++u) {
        for (auto v : e[u]) vistime[v] = u;
        for (auto v : e[u]) {
          for (auto w : e[v]) if (vistime[w] == u) {
            ++ans;
          }
        }
      }
      printf("%d\n", ans);
      return 0;
    }
    

    • 1

    信息

    ID
    5090
    时间
    1000ms
    内存
    128MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者