Gokix

一言(ヒトコト)

关注我

无向图三元环计数

在翻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;
}
posted @ 2022-07-09 20:57  Gokix  阅读(84)  评论(0编辑  收藏  举报