P1989 无向图三元环计数

P1989 无向图三元环计数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

属于是只有大文学家能写出来,我只能抄在积累本上的那种。

考虑给每个点赋权 aua_u,权值两两不同,然后给原图定向:

对于原图上的一条边 (u,v)(u, v),让 aa 小的连向 aa 大的。

不难发现形成的有向图是 DAG,因为如果 uvwuu \to v \to w \to u 就有 au<av<aw<aua_u < a_v < a _w < a_u,矛盾。

所以原来的三元环在新图中一定是这样一个形态:(uv),(uw),(vw)(u \to v), (u \to w), (v \to w)。没有其他形态,不信你画画看。

我们只要枚举 uu 的出点 vv,再枚举 vv 的出点 ww,检查 ww 是否是 uu 的出点即可。

只需枚举 uu 时,将 uu 的所有出点打上标记,就可以 O(1)\mathcal{O}(1) 判断 ww 是否为 uu 的出点。

现在,我们将 uu 的权值 aua_u 赋为 (du,u)(d_u, u)dud_u 表示 uu 在原图上的度数。

这是一个有序数对,比较时优先比较第一维再比较第二维,容易看出,因为第二维的存在,权值有了互异性。

如此以来可以保证:新图中每个点的出度不大于 m\sqrt m。简单证明一下。对于点 uu

  • 如果 dumd_u \le \sqrt m,显然新图上 uu 的出度不会超过 dud_u,所以新出度不大于 m\sqrt m
  • 如果 du>md_u > \sqrt m,因为 d=m\sum d = m,因此 dd 大于 m\sqrt{m} 的点不会超过 m\sqrt m 个,大于 dud_u 的点则更不会超过 m\sqrt m 个。根据我们的连边规则,新出度也不会大于 m\sqrt m

枚举 uu 和枚举其出点 vv 的二重循环可看作枚举边,复杂度为 mm;而枚举 vv 的出点复杂度为 m\sqrt m。所以,该算法时间复杂度为 Θ(mm)\Theta(m \sqrt m)

更帅的写法是 Θ(m1.5)\Theta(m^{1.5})(????)。

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2023-01-04 13:51:33 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2023-01-04 13:56:20
 */
#include <bits/stdc++.h>
inline int read() {
    int x = 0;
    bool f = true;
    char ch = getchar();
    for (; !isdigit(ch); ch = getchar())
        if (ch == '-')
            f = false;
    for (; isdigit(ch); ch = getchar())
        x = (x << 1) + (x << 3) + ch - '0';
    return f ? x : (~(x - 1));
}

const int maxn = (int)1e5 + 5;
const int maxm = (int)2e5 + 5;

int d[maxn], us[maxm], vs[maxm];
std :: vector <int> G[maxn];

int t[maxn];

int main() {
    int n = read(), m = read();
    for (int i = 1; i <= m; ++i) {
        int u = read(), v = read();
        us[i] = u;
        vs[i] = v;
        ++d[u];
        ++d[v];
    }

    for (int i = 1; i <= m; ++i) {
        int u = us[i], v = vs[i];
        if (d[u] > d[v])
            std :: swap(u, v);
        else if (d[u] == d[v] && u > v)
            std :: swap(u, v);
        G[u].push_back(v);
    }

    int ans = 0;
    for (int u = 1; u <= n; ++u) {
        for (int v : G[u])
            t[v] = u;
        for (int v : G[u])
            for (int w : G[v])
                if (t[w] == u)   
                    ++ans;
    }
    
    printf("%d\n", ans);
    return 0;
}
posted @   dbxxx  阅读(42)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示