【学习笔记】无向图、有向图的三元环、四元环计数问题(根号分支+bitset)
三元環計數和四元環計數問題
- 無向圖三元環計數問題
- 根號分治
- bitset
- 無向圖四元環計數問題
- 有向圖三四元環計數問題
無向圖三元環計數問題
根號分治
記 di:id_i:idi?:i 在原圖中的度數。
按照以下規則改寫無向圖為有向圖:
- 對于一條邊 u,vu,vu,v。
- 如果兩點度數不同,則度數大的指向度數小的。
- 如果兩點度數相同,則編號小的指向編號大的。
「其實只需要定一個有序的規則即可,可以是度數小的指向大的,可以是編號大的指向小的,都不影響。」
顯然,這個有向圖同時也是個無環圖。
證明:
假設存在一個環 (x0,x1,x2,...,xk)(x_0,x_1,x_2,...,x_k)(x0?,x1?,x2?,...,xk?)。
則有 dx0≥dx1≥dx2≥...≥dxkd_{x_0}\ge d_{x_1}\ge d_{x_2}\ge ...\ge d_{x_k}dx0??≥dx1??≥dx2??≥...≥dxk??。
即 dx0=...=dxkd_{x_0}=...=d_{x_k}dx0??=...=dxk??。
所以 x0<x1<x2<...<xk<x0x_0<x_1<x_2<...<x_k<x_0x0?<x1?<x2?<...<xk?<x0?,矛盾。
所以三元環 (u,v,w)(u,v,w)(u,v,w) 在新圖中的邊一定是:u→v,u→w,v→wu\rightarrow v,u\rightarrow w,v\rightarrow wu→v,u→w,v→w。
考慮在 uuu 點記錄這個三元環的貢獻。
將所有與 uuu 直接相連的點打上標記,再從中枚舉點作 vvv ,枚舉與 vvv 直接相連的點 www ,檢查 www 是不是 uuu 指向的點即可。
時間復雜度為 O(mm)O(m\sqrt{m})O(mm?)。
證明:
將枚舉 www 的復雜度記在 u→vu\rightarrow vu→v 邊上,顯然為 outvout_voutv? 。「outv:vout_v:voutv?:v 的出度」
則總復雜度為 ∑(u,v)∈Eoutv+∑uoutu\sum_{(u,v)\in E}out_v+\sum_{u}out_u∑(u,v)∈E?outv?+∑u?outu?
-
若 outv<mout_v<\sqrt{m}outv?<m?,則復雜度為 O(mm)O(m\sqrt{m})O(mm?)。
-
若 outv>mout_v>\sqrt{m}outv?>m?,因為有向圖的連邊規則,有 outu≥outv>mout_u\ge out_v>\sqrt{m}outu?≥outv?>m?。
原無向圖的總邊數為 mmm,所以這樣的 (u,v)(u,v)(u,v) 僅有 m\sqrt{m}m? 個,復雜度為 OmO\sqrt{m}Om?。
#include <bits/stdc++.h> using namespace std; #define maxn 100005 #define maxm 200005 vector < int > G[maxn]; int n, m; int d[maxn], Eu[maxm], Ev[maxm], vis[maxn];bool cmp( int x, int y ) {return d[x] == d[y] ? x < y : d[x] > d[y]; }int main() {scanf( "%d %d", &n, &m );for( int i = 1;i <= m;i ++ ) {scanf( "%d %d", &Eu[i], &Ev[i] );d[Eu[i]] ++, d[Ev[i]] ++;}for( int i = 1;i <= m;i ++ )if( cmp( Eu[i], Ev[i] ) ) G[Eu[i]].push_back( Ev[i] );else G[Ev[i]].push_back( Eu[i] );int ans = 0;for( int i = 1;i <= n;i ++ ) {for( int j : G[i] ) vis[j] = i;for( int j : G[i] )for( int k : G[j] ) if( vis[k] == i ) ans ++;}printf( "%d\n", ans );return 0; }bitset
bitset 其實是一種二進制的優化。
只要想要的信息可以轉化為被 0/10/10/1 表示出來,在空間不炸的情況下,bitset 自帶 /ω/\omega/ω 的復雜度。
將與 iii 點直接相連的所有點對應在 iii 的 bitset 中位置置為 111,表示兩點有邊相連。
然后就直接枚舉每條邊,將邊的兩個端點的 bitset 取個 &,就得到了與兩點均直接相連的所有點集合。
空間復雜度為 O(n2)O(n^2)O(n2) ,來自 bitset。
時間復雜度為 O(nmω)O(\frac{nm}{\omega})O(ωnm?)。
如果有些題目空間比較卡,不能直接硬開。
其實是給每個點很大一段的 000 開完整 bitset 空間就會被大幅度浪費。
那么就可以設定一個閾值 BBB「分塊的閾值」。
對于度數 >B>B>B 的點就開完整的 bitset,這種點不會超過 min?(n,mB)\min(n, \frac{m}{B})min(n,Bm?) 個。
度數 ≤B\le B≤B 的點就直接暴力枚舉連邊,暴力判斷。
所有度數 >B>B>B 的點最多會被 O(m)O(m)O(m) 條邊枚舉到,一次計算是 O(mω)O(\frac{m}{\omega})O(ωm?),時間復雜度為 O(nmω)O(\frac{nm}{\omega})O(ωnm?)。
度數 ≤B\le B≤B 的點的貢獻是 d2d^2d2,則有 ∑d≤m,d≤B\sum_d\le m,d\le B∑d?≤m,d≤B,總共為 ∑d2≤B?m\sum d^2\le B·m∑d2≤B?m。
時間復雜度為 O(nmω+B?m)O(\frac{nm}{\omega}+B·m)O(ωnm?+B?m),空間復雜度就降為了 O(nmB)O(\frac{nm}{B})O(Bnm?)。
「根據具體題目的時空靈活選取閾值 BBB,在不甚了解三元環的根號分治時,bitset 這種優美的暴力來的快也好寫/doge」
無向圖四元環計數問題
同樣按照三元環計數問題的方法來建立有向圖,并給每一個點分配唯一的 rankrankrank 排名。
則一個四元環在新圖中至少有一個,至多有兩個度數為 222 的點。
這時會發現原圖的四元環 (u,v,w,x)(u,v,w,x)(u,v,w,x) 在新建的有向圖中會有兩種情況。
- u→v→w,u→x→wu\rightarrow v\rightarrow w,u\rightarrow x\rightarrow wu→v→w,u→x→w
- u→v→w→x,u→xu\rightarrow v\rightarrow w\rightarrow x,u\rightarrow xu→v→w→x,u→x
發現這兩種形式都可以由 兩條無向邊+兩條有向邊 形式表示。
所以一個四元環唯一的表示為:u?v→w,u?x→wu-v\rightarrow w,u-x\rightarrow wu?v→w,u?x→w,且 rank(u)rank(u)rank(u) 最小,rank(w)rank(w)rank(w) 最大,這樣才能保證一個四元環不被反復計算。
枚舉點 uuu,枚舉其在原圖的出邊 vvv,枚舉 vvv 的在新圖上所有出邊 www,且要滿足 rank(u)<rank(v)<rank(w)rank(u)<rank(v)<rank(w)rank(u)<rank(v)<rank(w)。
先 ans←cntwans\leftarrow cnt_wans←cntw?,再 cntw++cnt_w++cntw?++,最后 uuu 更換的時候,所有 cntcntcnt 都要清零。
在上述過程中,cntw:wcnt_w:wcntw?:w 的入度,且 rank(w)rank(w)rank(w) 最大 rank(u)rank(u)rank(u) 最小,那么當有兩組 (u,v1,w),(u,v2,w)(u,v_1,w),(u,v_2,w)(u,v1?,w),(u,v2?,w) 的時候代表找到了一個四元環。
雖然環有對稱性,但這樣處理后一個四元環只會從 uuu 算到 www 上。
時間復雜度為 O(mm)O(m\sqrt{m})O(mm?)。證明與三元環一樣。
#include <bits/stdc++.h> using namespace std; #define maxn 100005 vector < int > G[maxn], E[maxn]; int n, m; int d[maxn], id[maxn], rnk[maxn], cnt[maxn];int main() {scanf( "%d %d", &n, &m );for( int i = 1, u, v;i <= m;i ++ ) {scanf( "%d %d", &u, &v );E[u].push_back( v );E[v].push_back( u );d[u] ++, d[v] ++, id[i] = i;}sort( id + 1, id + n + 1, []( int x, int y ) { return d[x] == d[y] ? x < y : d[x] > d[y]; } );for( int i = 1;i <= n;i ++ ) rnk[id[i]] = i;for( int i = 1;i <= n;i ++ ) for( int j : E[i] ) if( rnk[j] > rnk[i] ) G[i].push_back( j );int ans = 0;for( int i = 1;i <= n;i ++ ) {for( int j : E[i] ) for( int k : G[j] ) if( rnk[k] > rnk[i] ) ans += cnt[k], cnt[k] ++;for( int j : E[i] ) for( int k : G[j] ) cnt[k] = 0;} printf( "%d\n", ans );return 0; }有向圖三四元環計數問題
按照無向圖的方法做,求出一個元環后直接隨便選一個點開始左右兩個方向都走一圈,看能不能回到開始點,就知道無向圖上的元環能否用原有向圖上的邊構造出來。
總結
以上是生活随笔為你收集整理的【学习笔记】无向图、有向图的三元环、四元环计数问题(根号分支+bitset)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: usdt是什么币 usdt介绍
- 下一篇: 小时代中顾准扮演者是谁 小时代简介