P1989 三元环计数
三元环计数link
给定 nnn 个点,mmm 条边无向图,求其三元环个数。不保证图联通。n≤105,m≤2×105n \leq 10^5,m \leq 2\times 10^5n≤105,m≤2×105
先给每个点记录自己的度数,接着按照度数小向度数大连边,度数相同则按照编号从小往大连边的规则连边,最后我们要求的三元环 <u,v,w><u,v,w><u,v,w>一定满足有 <u,v>,<u,w>,<v,w><u,v>,<u,w>,<v,w><u,v>,<u,w>,<v,w>。
直接枚举 uuu,再按边找 vvv,按 vvv 的边找 www,最后若 www 与 uuu 相连,则找到。这里我们可以通过在枚举 uuu 时对与其相连的所有点都打上标记的方式来方便 O(1)O(1)O(1) 查找 www 与 uuu 的相连情况。
这样就非常巧妙地做完了,时间复杂度 O(mm)O(m \sqrt m)O(mm)。
证明如下:
若 vvv 的度数小于根号,我们知道新图的度数一定不大于原图度数,所以 vvv 的出边的个数最多也小于根号。
若 vvv 的度数大于等于根号,又因为我们把度数小的边向度数大的边连边,所以从 vvv 出发相连的点度数都大于根号,而这些点最多不会超过根号。
所以最终为 O(mm)O(m \sqrt m)O(mm)。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+5;
const int M=2e5+5;
int n,m;
struct node{int v,nxt;
}e[M],e2[M];
int h[M],tot;
int d[N];
int vis[N];
int ox[N],oy[N];
int ans;
void add(int x,int y){e[++tot].nxt=h[x];e[tot].v=y;h[x]=tot;
}
int main(){memset(h,-1,sizeof h);scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){scanf("%d%d",&ox[i],&oy[i]);d[ox[i]]++,d[oy[i]]++;}for(int i=1;i<=m;i++){if(d[ox[i]]==d[oy[i]]){add(min(ox[i],oy[i]),max(ox[i],oy[i]));}else{if(d[ox[i]]<d[oy[i]]) add(ox[i],oy[i]);else add(oy[i],ox[i]);}}for(int u=1;u<=n;u++){for(int i=h[u];~i;i=e[i].nxt){vis[e[i].v]=1;}for(int i=h[u];~i;i=e[i].nxt){int v=e[i].v;for(int j=h[v];~j;j=e[j].nxt){if(vis[e[j].v]==1) ans++;}}for(int i=h[u];~i;i=e[i].nxt){vis[e[i].v]=0;}}printf("%d\n",ans);return 0;
}