无向图三元环计数
在翻SDSC2021Day2笔记时发现了这个东西
然而当天讲课的主题是数据结构
直接在原图上 \(O(n^2)\) 的枚举显然是过不了的。
考虑给图定向。
我们记 \(w_i\) 表示节点 \(i\) 的出入度之和,边 \(u \leftrightarrow v\) 成为 \(u \to v\) 当且仅当 \(w_u<w_v \space \text{or} \space (w_u=w_v \space \text{and} \space u<v)\) ,否则成为 \(u \leftarrow v\)。
假设三元环 \(<x,y,z>\) 满足 \(w_x<w_y<w_z\),则其重定向后三边为 \(x \to y,x \to z,y \to z\),于是枚举方式显然:
我们枚举所有节点 \(i\),对其所有能到的节点打上标记。接着从 \(i\) 到达的节点 \(j\) 继续遍历,若 \(j\) 能到达的节点被打过标记,则 \(ans++\)。
注意到重定向后每个节点的出度不大于 \(\sqrt{m}\),所以这个方法的时间复杂度是 \(O(m \sqrt{m})\)。
bool cmp(int u,int v){
if(w[u]<w[v]) return 1;
if(w[u]==w[v] && u<v) return 1;
return 0;
}
int main(){
int i,j,u,v,k;
n=read();m=read();
for(i=1;i<=m;i++){
x[i]=read();y[i]=read();
w[x[i]]++,w[y[i]]++;
}
for(i=1;i<=m;i++){
if(cmp(x[i],y[i])) add(x[i],y[i]);
else add(y[i],x[i]);
}
for(i=1;i<=n;i++){
for(j=h[i];j;j=e[j].nxt){
vis[e[j].v]=1;
}
for(j=h[i];j;j=e[j].nxt){
for(u=h[e[j].v];u;u=e[u].nxt){
if(vis[e[u].v]) ans++;
}
}
for(j=h[i];j;j=e[j].nxt){
vis[e[j].v]=0;
}
}
cout<<ans<<endl;
return 0;
}