不常用的黑科技——「三元环」
万恶之源:
给定一张无重边、无自环的无向图(点数为n,边数为m,且n,m同阶),问有多少个无序三元组(i,j,k),使得存在:
1. 有一条连接i,j的边
2. 有一条连接j,k的边
3. 有一条连接k,i的边
举个例子:
这张图中有三个三元环:(1,2,3),(1,3,4),(3,4,5)
有一个显然的基于度数的乱搞的做法(本人并没有写过……),可以在这里阅读到:jiachin_zhao [hdu 6184 Counting Stars](三元环计数)
这里介绍一种十分优秀的三元环计数方法:
首先要对所有的无向边进行定向,对于任何一条边,从度数大的点连向度数小的点,如果度数相同,从编号小的点连向编号大的点
此时这张图是一个有向无环图
之后枚举每一个点u,然后将u的所有相邻的点都标记上“被u访问了”,然后再枚举u的相邻的点v,然后再枚举v的相邻的点w,如果w存在“被u访问了”的标记,那么(u,v,w)就是一个三元环了
而且每个三元环只会被计算一次
那么现在就只需要证明三件事:
1. 定向后的图是一个有向无环图
以上图为例,用degu表示u在原图中的度数,则dega≥degb≥degc≥degd≥dege≥dega
既dega=degb=degc=degd=dege=dega
对于度数相同的点,是按照编号从小到大连边的,则a≤b≤c≤d≤e≤a
既a=b=c=d=e
然而点的编号是1∼n的全排列,因此不会产生如上的事情,既不存在环
由于这张图有向,而且没有环,因此这张图就是有向无环图
2. 时间复杂度是O(m√m)(在此认为n,m同阶)
对于“打标记”这个操作,每个点都会将所有的出边遍历一遍,那么这里的时间复杂度为O(n+m)
对于访问u,然后访问v,然后访问w,可以这么考虑:对于每条边(u→v),对时间复杂度的贡献为outv,其中outv表示v的出边个数
这样就转化为了求∑ni=1outi
不妨将outv分类讨论
1. outv≤√m,那么由于u连接v,因此degu≥degv,这样的uu的个数是O(n)的,因此这里的时间复杂度为O(n√m)
2. outv>√m,那么由于u连接v,因此degu≥degv,既degu>√m,这样的u的个数是O(√m)的,因此这里的时间复杂度为O(√mm)=O(m√m)
因此时间复杂度为O(n+m+n√m+m√m)=O(m√m)
3. 每个三元环只会被统计一次
三元环在有向无环图上无非就长这样:
也只能且只会在u处被计算一次

1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 1e5 + 10; 4 vector<int> g[N]; 5 int deg[N], vis[N], n, m, ans; 6 struct E { int u, v; } e[N * 3]; 7 int main() { 8 scanf("%d%d", &n, &m); 9 for(int i = 1 ; i <= m ; ++ i) { 10 scanf("%d%d", &e[i].u, &e[i].v); 11 ++ deg[e[i].u], ++ deg[e[i].v]; 12 } 13 for(int i = 1 ; i <= m ; ++ i) { 14 int u = e[i].u, v = e[i].v; 15 if(deg[u] < deg[v] || (deg[u] == deg[v] && u > v)) swap(u, v); 16 g[u].push_back(v); 17 } 18 for(int x = 1 ; x <= n ; ++ x) { 19 for(auto y: g[x]) vis[y] = x; 20 for(auto y: g[x]) 21 for(auto z: g[y]) 22 if(vis[z] == x) 23 ++ ans; 24 } 25 printf("%d\n", ans); 26 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)