(v4 更新)0x60 图论

0x61 图论 Misc

三元环计数、四元环计数

无向图三元环计数

  • 将所有点按照度数从小到大排序(度数相同时任意排序),将所有边按照排名小指向排名大的规则定向,形成一个 DAG。
  • 此时所有三元环的边向情况,只有 \(x \to y\)\(y \to z\)\(x \to z\) 这一种情况。
  • 枚举点 \(x\)。第一轮枚举 \(x\) 在 DAG 中的出边 \(z\),将 \(z\) 打上标记;第二轮枚举 \(x\) 在 DAG 中的出边 \(y\),再枚举 \(y\) 在 DAG 中的出边 \(z\)。如果 \(z\) 存在标记,则 \(x, y, z\) 构成三元环。

无向图三元环计数 时间复杂度\(\mathcal{O}(m\sqrt{m})\)

  • \(\mathrm{deg}_u\) 为原图中点 \(u\) 的度,\(\mathrm{out}_u\) 为 DAG 中点 \(u\) 的出度。可以证明 \(\mathrm{out}_u\)\(\mathcal{O}(\sqrt{m})\) 级别。
    • \(\mathrm{deg}_u \leq \sqrt{m}\),则 \(\mathrm{out}_u \leq \sqrt{m}\)
    • \(\mathrm{deg}_u > \sqrt{m}\),由于 \(u\) 在 DAG 中指向的点的度数大于 \(\sqrt{m}\),这样的点不超过 \(\sqrt{m}\) 个,故 \(\mathrm{out}_u \leq \sqrt{m}\)
  • 上述枚举的时间复杂度,相当于对 DAG 中每条边 \((u, v)\)\(\mathrm{out}_v\) 求和。每条边的贡献为 \(\mathcal{O}(\sqrt{m})\),故时间复杂度为 \(\mathcal{O}(m\sqrt{m})\)

无向图三元环计数的推论:无向图的三元环个数为 \(\mathcal{O}(m\sqrt{m})\) 级别。

0x61 无向图三元环计数.cpp

// 无向图三元环计数
void ring3() {
    std::vector<int> deg(n + 1);
    for (auto [x, y] : edges) {
        deg[x] ++, deg[y] ++;
    }

    std::vector< std::vector<int> > out(n + 1);
    for (auto &[x, y] : edges) {
        if (deg[x] > deg[y] || (deg[x] == deg[y] && x > y)) {
            std::swap(x, y);
        }
        out[x].push_back(y);
    }

    std::vector<int> vis(n + 1);
    for (int x = 1; x <= n; x ++) {
        for (int z : out[x]) {
            vis[z] = 1;
        }
        for (int y : out[x]) {
            for (int z : out[y]) {
                if (vis[z]) {
                    // 找到一个三元环 x, y, z
                }
            }
        }
        for (int z : out[x]) {
            vis[z] = 0;
        }
    }
}

无向图四元环计数

  • 将所有点按照度数从小到大排序(度数相同时任意排序),将所有边按照排名小指向排名大的规则定向,形成一个 DAG。
  • 我们认为一个四元环的贡献,仅在排名最大的那个点 \(z\) 处被统计到。此时所有四元环的边向情况,都可以认为是两条形如 \(x \leftrightarrow y, y \to z\) 的路径拼起来的情况(其中 \(x \leftrightarrow y\) 是原图中的边,\(y \to z\) 是 DAG 中的边,并且 \(z\) 是四个点中排名最大的点)。
  • 枚举点 \(x\)。再枚举 \(x\) 在原图中的边 \(y\),再枚举 \(y\) 在 DAG 中的边 \(z\)(此时我们应该保证 \(\mathrm{rk}_x < \mathrm{rk}_z\)),则先前枚举的 \(x \leftrightarrow y, y \to z\) 路径均与当前枚举的路径构成四元环。

无向图四元环计数 时间复杂度\(\mathcal{O}(m\sqrt{m})\),分析基本同无向图三元环计数。

0x61 无向图四元环计数.cpp

// 无向图四元环计数
void ring4() {
    std::vector<int> deg(n + 1);
    for (auto [x, y] : edges) {
        deg[x] ++, deg[y] ++;
    }

    std::vector< std::vector<int> > out(n + 1);
    for (auto &[x, y] : edges) {
        if (deg[x] > deg[y] || (deg[x] == deg[y] && x > y)) {
            std::swap(x, y);
        }
        out[x].push_back(y);
    }

    i64 ans = 0;
    std::vector<int> cnt(n + 1);
    for (int x = 1; x <= n; x ++) {
        for (int y : G[x]) {
            for (int z : out[y]) {
                if (deg[x] < deg[z] || (deg[x] == deg[z] && x < z)) {
                    // 找到一条 x<->y, y->z 的路径
                    ans += cnt[z] ++;
                }
            }
        }
        for (int y : G[x]) {
            for (int z : out[y]) {
                if (deg[x] < deg[z] || (deg[x] == deg[z] && x < z)) {
                    cnt[z] = 0;
                }
            }
        }
    }
}

有向图三(四)元环计数:转换为无向图三(四)元环计数,找到环时判断方向即可。

竞赛图三元环计数:记点 \(i\) 的出度为 \(\mathrm{out}_i\),则竞赛图的三元环个数为

\[\binom{n}{3} - \sum_{i = 1}^n \binom{\mathrm{out}_i}{2} \]

Prufer 序列

Prufer 序列:一个包含 \(n - 2\) 个取值范围在 \([1, n]\) 中的正整数序列。可以理解为有标号完全图的生成树数列的双射。

Prufer 序列构造:每次选择一个编号最小的叶子然后删掉它,并且在序列中记录与它相连的那个节点。重复 \(n - 2\) 次以后,只剩下两个节点时停止。

Prufer 序列性质

  • Prufer 序列构造完毕后会剩下两个节点,其中一个必定是编号最大的 \(n\)。而另一个则是 \(n\)\(n - 1\) 路径上的第一个点。
  • 每个节点在 Prufer 序列中的出现次数,等于其度数减 \(1\)

Cayley 公式:对于包含 \(n\) 个点的完全图,共有 \(n^{n - 2}\) 棵生成树。

扩展 Cayley 公式 1:对于包含 \(n\) 个点 \(m\) 条边的森林,设其有 \(k\) 个连通块,大小分别为 \(s_1, s_2, \cdots, s_k\),则可以使得森林变成树的加边方式共有

\[n^{k - 2}\prod_{i = 1}^k s_i \]

扩展 Cayley 公式 2:对于 \(n\) 个有标号节点,形成恰好包含 \(s\) 棵树的森林,如果实现指定了 \(s\) 个节点,并要求这 \(s\) 个节点两两不在同一棵树中,则满足该条件的森林方案总数共有

\[sn^{n - s - 1} \]

posted @ 2022-12-19 10:23  Calculatelove  阅读(360)  评论(0)    收藏  举报