====== 三元环计数 ====== ===== 算法例题 ===== ==== 例题一 ==== [[https://www.luogu.com.cn/problem/P1989|洛谷p1989]] === 题意 === 给定 $n$ 个点和 $m$ 条边的无向图。定义三元环为满足所有点两两之间有连边的三元组数,求图中的三元环数。 === 题解 === 将无向图转化为有向图,其中每条边由原图中度数小的点指向度数大的点,当两个点度数相同时由编号小的点指向编号大的点。 由于上述定义指三元环为满足 $(u,v,w)$ 一定满足连边为 $u\to v,u\to w,v\to w$。 考虑枚举 $u$,标记 $u\to w$ 的 $w$ 后枚举 $v$ 然后检查 $v\to w$ 的 $w$ 是否被标记。 时间复杂度为 $O(\sum_{i=1}^m\text{out}_{v_i})$,若原图中 $\text{deg}_{v_i}\le \sqrt m$,则当前图中一定也有 $\text{out}_{v_i}\le \sqrt m$。 若原图中 $\text{deg}_{v_i}\gt \sqrt m$,由于与当前图中 $v_i$ 相邻的点一定满足 $\text{deg}_w\ge \text{deg}_{v_i}\gt \sqrt m$,于是这样的 $w$ 不超过 $\sqrt m$ 个,即也有 $\text{out}_{v_i}\le \sqrt m$。 综上,总时间复杂度为 $O\left(m\sqrt m\right)$。 const int MAXN=1e5+5,MAXM=2e5+5; struct Edge{ int to,next; }edge[MAXM]; int head[MAXN],edge_cnt; void Insert(int u,int v){ edge[++edge_cnt]=Edge{v,head[u]}; head[u]=edge_cnt; } int deg[MAXN],vis[MAXN]; pair E[MAXM]; int main() { int n=read_int(),m=read_int(); _for(i,0,m){ int u=read_int(),v=read_int(); deg[u]++,deg[v]++; E[i]=make_pair(u,v); } _for(i,0,m){ int u=E[i].first,v=E[i].second; if(deg[u] ==== 例题二 ==== [[http://acm.hdu.edu.cn/showproblem.php?pid=6184|HDU6184]] === 题意 === 给定 $n$ 个点和 $m$ 条边的无向图。定义好的四元组 $(A,B,C,D)$ 为满足存在连边 $AB,BC,CD,DA,AC$ 的四元组,求图中好的四元组数。 === 题解 === 统计每条边出现在三元组中的次数 $c_i$,最终答案为 $\sum_{i=1}^n {c_i\choose 2}$ const int MAXN=1e5+5,MAXM=2e5+5; struct Edge{ int to,next,c; }edge[MAXM]; int head[MAXN],edge_cnt; void Insert(int u,int v){ edge[++edge_cnt]=Edge{v,head[u],0}; head[u]=edge_cnt; } int deg[MAXN],vis[MAXN],in[MAXN]; pair E[MAXM]; int main() { int n,m; while(cin>>n>>m){ _rep(i,1,n)head[i]=deg[i]=vis[i]=in[i]=0; edge_cnt=0; _for(i,0,m){ int u=read_int(),v=read_int(); deg[u]++,deg[v]++; E[i]=make_pair(u,v); } _for(i,0,m){ int u=E[i].first,v=E[i].second; if(deg[u]