日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

三元环计数

發布時間:2024/1/1 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 三元环计数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

算法如其名,就是用來找三元環的。

介紹

給出一張無向圖,問圖中有多少個三元組{x,y,z}\{x,y,z\}{x,y,z},滿足圖中存在 {x?y,y?z,z?x}\{x-y,y-z,z-x\}{x?y,y?z,z?x} 三條邊。

轉化

考慮將這張圖轉化為有向圖:對于一條無向邊 x?yx-yx?y,不妨設點 xxx 的度大于點 yyy 的度,那么就將這條無向邊變成 x→yx \to yxy。假如兩點的度相同,就從編號大的往編號小的連邊。

這樣轉化后,這張圖一定是有向無環圖。

證明:

使用反證法,假設有一個環:a→b→c→aa\to b \to c \to aabca,那么有(設 xxx 的度為 dxd_xdx?):da≥db≥dc≥dad_a \geq d_b \geq d_c \geq d_ada?db?dc?da?,要使該不等式成立,當且僅當滿足 da=db=dc=dad_a=d_b=d_c=d_ada?=db?=dc?=da?

xxx 的編號為 idxid_xidx?,那么有:ida>idb>idc>idaid_a>id_b>id_c>id_aida?>idb?>idc?>ida?,即 ida>idaid_a>id_aida?>ida?,該柿子不成立,故假設不成立,證畢

求解

步驟如下:

  • 枚舉每一個點,對于當前的點,將它能到達的點做上標記
  • 做完標記后,枚舉每個被做過標記的點,對于當前被做過標記的點,枚舉他能到達的點
  • 假如這個能到達的點是被做過標記的,那么就找到了一個三元環,答案+1+1+1
  • 正確性

    轉化完后,每一個三元環會變成下面這樣的東西:

    只有枚舉到紅點的時候,這個三元環才會被計算到,這就保證了不會重復計算并且不會漏算。

    時間復雜度

    考慮每一條邊被遍歷的次數:對于一條邊 x→yx\to yxy,他被遍歷的次數為 inxin_xinx?。(inxin_xinx?表示 xxx 的入度),那么總的時間復雜度就是每一條邊的 inxin_xinx? 之和。

    又可以發現,inxin_xinx? 的上限就是 m\sqrt mm?,因為要求每個連向 xxx 的點的度都大于 inxin_xinx?,也就是說,有 inxin_xinx? 個點的度大于 inxin_xinx?,這樣就至少需要 inx2in_x^2inx2? 條邊,所以 inx2≤m?inx≤min_x^2\leq m \Rightarrow in_x\leq \sqrt minx2?m?inx?m?

    例題

    題目傳送門

    顯然,題目中要求的四元組,也就是兩個有一條公共邊的三元環拼在一起,那么求出每條邊在多少個三元環中(設 totitot_itoti? 表示第 iii 條邊在多少個三元環里),然后 ∑i=1mCtoti2\sum_{i=1}^m C_{tot_i} ^2i=1m?Ctoti?2? 就是答案。

    代碼如下:

    #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define maxn 300010 #define ll long longint n,m; struct edge{int y,next;}; edge e[maxn*4]; int len; int first[maxn]; void buildroad(int x,int y) {e[++len]=(edge){y,first[x]};first[x]=len; } struct node{int x,y;}; node edges[maxn*2]; int du[maxn],id[maxn],to[maxn],tot[maxn]; inline ll C(int x){return (ll)x*(x-1)/2ll;}int main() {while(~scanf("%d %d",&n,&m)){memset(du,0,sizeof(du));//記錄每個點的度的數組for(int i=1;i<=m;i++)scanf("%d %d",&edges[i].x,&edges[i].y),du[edges[i].x]++,du[edges[i].y]++;memset(first,0,sizeof(first));len=0;for(int i=1,x,y;i<=m;i++){x=edges[i].x,y=edges[i].y;if(x>y)swap(x,y);//讓x成為編號小的點if(du[x]>=du[y])buildroad(x,y);//度大的往小的連有向邊else buildroad(y,x);}memset(tot,0,sizeof(tot));//別忘了初始化各種數組memset(to,0,sizeof(to));//to[i]表示當前點到點i的邊是第幾條邊memset(id,0,sizeof(id));//id表示每個點的標記for(int i=1;i<=n;i++){int x=i;for(int j=first[x];j;j=e[j].next)//枚舉能到達的點,給他們大商標機id[e[j].y]=x,to[e[j].y]=j;//打標記,記錄tofor(int j=first[x];j;j=e[j].next)//再次遍歷所有有標記的點{for(int k=first[e[j].y];k;k=e[k].next)//遍歷有標記的點能到達的點if(id[e[k].y]==x)tot[j]++,tot[k]++,tot[to[e[k].y]]++;//假如能到達一個有標記的點,那么就找到了一個三元環,給這三條邊都打上標記}}ll ans=0;for(int i=1;i<=len;i++)//統計答案ans+=C(tot[i]);printf("%lld\n",ans);} }

    總結

    以上是生活随笔為你收集整理的三元环计数的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。