稀疏圖三元環計數
阿新 • • 發佈:2020-09-15
度數分治
將度數 \(\ge \sqrt m\) 的點稱為大點,度數 \(\lt \sqrt m\) 的點稱為小點。
列舉點 \(a\),
Case 1:\(a\) 是大點,標記與 \(a\) 相連的小點和與 \(a\) 相連的編號比 \(a\) 小的大點,列舉圖中的每一條邊,判斷兩端點是否都被標記。\(O(m \sqrt m)\)
Case 2:\(a\) 是小點,列舉與 \(a\) 相連的編號比 \(a\) 小的小點 \(b\),列舉與 \(b\) 相連的編號比 \(a\) 小的小點 \(c\),判斷 \(c\) 是否被標記,標記點 \(b\)。\(O(m \sqrt m)\)
所有包含大點的三元環都只會在編號最大的大點處被統計,所有隻包含小點的三元環都只會在編號最大的點處被統計,不重不漏。
Case 1 的複雜度顯然。Case 2 中每條小點之間的邊 \((a, b)\) 都只會被算一次,貢獻 \(deg_b\) 的複雜度。
例題:HDU6184
#include<cstdio> #include<cmath> #include<cstring> #include<map> #include<algorithm> using namespace std; typedef long long ll; const int N = 100005; const int M = 200005; int n, m, sm, hd[N], cnt, deg[N]; struct Edge { int to, nxt; } e[M << 1]; struct Gedge { int u, v; } ge[M]; map<pair<int,int>, int> tot; bool tag[N]; void adde(int u, int v) { e[++cnt].to = v; e[cnt].nxt = hd[u]; hd[u] = cnt; e[++cnt].to = u; e[cnt].nxt = hd[v]; hd[v] = cnt; } #define mkpr(u, v) make_pair(min(u, v), max(u, v)) void work1(int u) { for(int i = hd[u]; i; i = e[i].nxt) { int v = e[i].to; if(deg[v] < sm || v < u) { tag[v] = 1; } } for(int i = 1; i <= m; ++i) { if(tag[ge[i].u] && tag[ge[i].v]) { ++tot[mkpr(u, ge[i].u)]; ++tot[mkpr(u, ge[i].v)]; ++tot[mkpr(ge[i].u, ge[i].v)]; } } for(int i = hd[u]; i; i = e[i].nxt) { tag[e[i].to] = 0; } } void work2(int u) { for(int i = hd[u]; i; i = e[i].nxt) { int v = e[i].to; if(deg[v] >= sm || v > u) continue; for(int j = hd[v]; j; j = e[j].nxt) { int w = e[j].to; if(tag[w]) { ++tot[mkpr(u, v)]; ++tot[mkpr(u, w)]; ++tot[mkpr(v, w)]; } } tag[v] = 1; } for(int i = hd[u]; i; i = e[i].nxt) { tag[e[i].to] = 0; } } int main() { while(~scanf("%d%d", &n, &m)) { cnt = 0; memset(hd, 0, sizeof(int) * (n + 1)); memset(deg, 0, sizeof(int) * (n + 1)); for(int i = 1; i <= m; ++i) { int u, v; scanf("%d%d", &u, &v); ge[i].u = u; ge[i].v = v; adde(u, v); ++deg[u]; ++deg[v]; } sm = (int)sqrt(m); tot.clear(); for(int u = 1; u <= n; ++u) { if(deg[u] >= sm) work1(u); else work2(u); } ll ans = 0; for(int i = 1; i <= m; ++i) { int tmp = tot[mkpr(ge[i].u, ge[i].v)]; ans += (ll)tmp * (tmp - 1) / 2; } printf("%lld\n", ans); } return 0; }