给定一个无向简单图 $G=(V,E)$,$|V|, |E| \le 1e5$。
问有多少个无序三元组 $(i,j,k)$,满足三个点两两有连边。
处理方法
- 将无向图转化成有向图,对于每条边: - 
- 度数大的点连向度数小的点;
- 度数相同,编号小的点连向编号大的点。
 
- 枚举每个点 $u$: - 
- 将 $u$ 相邻的每一个点打上标记;
- 枚举 $u$ 相邻的每一个点 $v$ 的相邻点 $w$,如果 $w$ 被打上了标记,那么 $(u,v,w)$ 即为一个要求的三元环。
 
证明
预处理后的图是一个有向无环图
假设图 $G=(V,E)$ 上存在一条环 $a_1, a_2,\dots,a_m,a_1$ ,那么
$$
deg(a_1) \ge deg(a_2) \ge \dots \ge deg(a_m) \ge deg(a_1)
$$
因此有
$$
deg(a_1)=deg(a_2)=\dots=deg(a_m)=deg(a_1) \
a_1<a_2< \dots < a_m < a_1
$$
显然矛盾。
枚举的时间复杂度为 $O(n \sqrt{n})$
对于枚举的每一个点 $u$ 。
第一次遍历打标记的时间复杂度为 $O(n+m)$。
第二次遍历,考虑每条边 $(u,v)$ 对复杂度的贡献是 $v$ 的出度,对于每个点 $v$ 的出度 $out(v)$:
- $out(v) \le \sqrt{m}$ 时,对时间复杂度的总贡献为 $O(m \sqrt{m})$ 。
- $out(v) > \sqrt{m}$ 时,$out(u) > out(v) > \sqrt{m}$,满足这个条件的边数不超过 $\sqrt{m}$ ,对时间复杂度的总贡献为 $O(m \sqrt{m})$ 。
因此,枚举的时间复杂度为 $O(n \sqrt{n} )$ 。
颓废了一个多月,滚回来看看组合QwQ。
例题
HDu6184 Counting Stars
求有多少四元组 $(a,b,c,d)$,满足有一条边是公共边的两个三元环。
将所有三元环抠出来,记录每条边能够成三元环的数量 $cnt$,那么它对答案的贡献为 $C(cnt,2)$。
注意预处理的连边方向!
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 
 | #include <iostream>#include <cstdio>
 #include <cstring>
 #include <algorithm>
 #include <vector>
 #include <map>
 #include <utility>
 #define assert(x) do{int a=1,b=0;if(!(x))printf("%d",a/b);}while(0)
 #ifdef XLor
 #define dbg(args...) do {cout << "debug -> "; err(args);} while (0)
 #else
 #define dbg(...)
 #endif
 void err() {std::cout << std::endl;}
 template<typename T, typename...Args>
 void err(T a, Args...args){std::cout << a << ' '; err(args...);}
 #define ms(a,b) memset(a,b,sizeof(a))
 using namespace std;
 typedef long long ll;
 typedef pair<int, int> PII;
 const int maxn = 300000 + 5;
 
 int n, m, u[maxn], v[maxn], deg[maxn], vis[maxn];
 vector<int> edge[maxn];
 
 int main(){
 while (~scanf("%d%d", &n, &m)) {
 ms(vis, 0); ms(deg, 0); for (int i = 0; i <= n; i++) edge[i].clear();
 for (int i = 0; i < m; i++) {
 scanf("%d%d", u + i, v + i);
 deg[u[i]]++; deg[v[i]]++;
 }
 for (int i = 0; i < m; i++) {
 if (deg[u[i]] != deg[v[i]]) {
 if (deg[u[i]] < deg[v[i]]) swap(u[i], v[i]);
 } else {
 if (u[i] > v[i]) swap(u[i], v[i]);
 }
 edge[u[i]].push_back(v[i]);
 }
 ll ans = 0;
 map<PII,int> mp;
 for (int i = 1; i <= n; i++) {
 for (int v: edge[i]) vis[v] = i;
 for (int v: edge[i]) {
 for (int u: edge[v]) if (vis[u] == i) {
 int a[3] = {i, u, v}; sort(a, a + 3);
 mp[{a[0], a[1]}]++; mp[{a[0], a[2]}]++; mp[{a[1], a[2]}]++;
 }
 }
 }
 for (auto& x: mp) {
 dbg(x.second);
 ans += 1ll * x.second * (x.second - 1) / 2;
 }
 printf("%lld\n", ans);
 }
 return 0;
 }
 
 |