赞
踩
题意:
给定一个无向图,定义一个三元环 ( u , v , w ) (u,v, w) (u,v,w) 满足: ( u , v ) , ( v , w ) , ( w , u ) (u, v),(v, w),(w, u) (u,v),(v,w),(w,u) ,求无向图中三元环的数量。 n ∈ [ 1 , 1 0 5 ] n \in[1, 10^5] n∈[1,105]
思路:
看题解的www
纯粹暴力枚举肯定不行,但是无向图的三元环又不好考虑,那么就将无向图的的边进行定向。
一条边的方向定义规则为:度数小的–>度数大的, 度数相同时,节点编号小的–>节点编号大的
该操作可以保证得到的新图中每个节点的 O u t ( v i ) ≤ m Out(v_i) \leq \sqrt m Out(vi)≤m 。
这样处理的好处是,对于一个三元环:
如果三个顶点的度数不相同,那么肯定存在一个顶点的入度>2或出度>2
如果三个顶点的度数相同,因为节点的编号肯定存在一个最小的,那么同样会有上一条的性质。
这样定向后的新图中,不会出现 < u , v > < v , w > < w , u > <u,v><v,w><w,u> <u,v><v,w><w,u> 这样的有向环,因为肯定存在一个顶点的入度或出度大于2。原图的三元环就会变成 < u , v > , < u , w > , < v , w > <u,v>,<u,w>,<v,w> <u,v>,<u,w>,<v,w> 。这样对于一个三元环中的三个点,只会存在一个节点 u u u,满足 < u , v > , < u , w > , < v , w > <u,v>,<u,w>,<v,w> <u,v>,<u,w>,<v,w>。这样就不会重复计数了。
然后就是枚举所有点,计算当前点 u u u 可达的点 v v v ,点 v v v 可达的点中是否存在 点 u u u 可达的点。
复杂度证明:
每个顶点的出度一定 ≤ m \leq \sqrt m ≤m :
乍一看,复杂度还是
O
(
n
×
m
×
m
)
O(n \times \sqrt m \times \sqrt m)
O(n×m
×m
)。反正是我就不敢写 但其实复杂度是
O
(
m
m
)
O(m\sqrt m)
O(mm
) 的。
对于每一条边,它的复杂度贡献都是 o u t ( v i ) out(v_i) out(vi) 那么总复杂度就是 ∑ i = 1 m o u t ( i ) = m m \sum_{i = 1}^{m}out(i) = m\sqrt m ∑i=1mout(i)=mm
代码:
#include<cstdio> #include<vector> #include<queue> #include<stack> #include<cmath> #include<map> #include<set> #include<cstring> #include<string> #include<algorithm> #define fi first #define se second #include<stdlib.h> #include <time.h> //srand((unsigned)time(NULL)); using namespace std; typedef long long ll; typedef unsigned long long ull; const int INF = 0x3f3f3f3f; using namespace std; const int N = 2e5 + 10; const double eps = 1e-7; struct edge{ int v, nxt; }es[N]; int head[N], tot; void add_edge(int u, int v) { tot++; es[tot].v = v; es[tot].nxt = head[u]; head[u] = tot; } int n, m; pair<int, int> a[N]; int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) head[i] = -1; vector<int> du(n + 1); for (int i = 1; i <= m; i++) { int u, v; scanf("%d%d", &u, &v); du[u]++; du[v]++; a[i].first = u; a[i].second = v; } for (int i = 1; i <= m; i++) { int u = a[i].first, v = a[i].second; if (du[u] > du[v] || du[u] == du[v] && u > v) swap(a[i].first, a[i].second); add_edge(a[i].first, a[i].second); } int ans = 0; vector<int> vis(n + 1, 0); for (int u = 1; u <= n; u++) { for (int i = head[u]; i != -1; i = es[i].nxt) vis[es[i].v] = 1; for (int i = head[u]; i != -1; i = es[i].nxt) { int v = es[i].v; for (int j = head[v]; j != -1; j = es[j].nxt) { if (vis[es[j].v]) ans++; } } for (int i = head[u]; i != -1; i = es[i].nxt) vis[es[i].v] = 0; } printf("%d\n", ans); return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。