三元环问题

三元环问题

解题思路: 度 = 入度 + 出度

根据 1、度小的连向度大的 2、若度相同,则编号小的连向编号大的

把所有的边建立成有向边(防止后面重复的统计三元环)

然后for循环遍历所有的边 判断所有的边的两端点,判断两个端点有没有连接,如果有连接,那么就ans++。

代码:

#include <iostream>
#include <cstring>
using namespace std;
const int N = 3e5+5,M=6e5+6;
int h[N],ne[M],e[M],idx;

void add(int a,int b){
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;	
}
int du[N];
int vis[N];
int X[M],Y[M];
//记录输入的一条边上的两个点 
typedef long long ll;
 
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=0;i<m;i++){
		scanf("%d%d",&X[i],&Y[i]);
		du[X[i]]++;
		du[Y[i]]++;		
	}
	//建立有向边的要求: 
	//数小的往度数大的走
	//度数相同则按编号(值)小到大走
	memset(h,-1,sizeof h); 
	for(int i=0;i<m;i++){
	//在此,将所有的边建立成有向边的形式 
		if(du[X[i]]<du[Y[i]]){
			add(X[i],Y[i]);
		}else if(du[Y[i]]<du[X[i]]){
			add(Y[i],X[i]);
		}else{//编号 
			if(X[i]<Y[i]){
				add(X[i],Y[i]);
			} else{
				add(Y[i],X[i]);
			}
		}
	}
	ll ans=0;
	for(int i=0;i<m;i++){
		int u=X[i],v=Y[i];//此时直线两端的两个点
		for(int j=h[u];j!=-1;j=ne[j]){
			int x=e[j];//u所连接的点进行标记 
			vis[x]=i+1;
		} 
		for(int j=h[v];j!=-1;j=ne[j]){
			int x=e[j];//v所连接的点 
			if(vis[x]){//如果发现这个点被标记过了,则表示形成了三元环 
				ans++;
			}
		}
	}
	cout<<ans<<endl;
	return 0;
} 

相关题目:

Counting Stars(2017广西邀请赛)

题目链接:Problem - 6184 (hdu.edu.cn)

解题思路:

记录每条边可以连接成的三元环的个数,如果个数小于等于1,那么可以形成的A-structure数量一定是0。

如果>=2,则这个边(作为中间那条边),可以形成的A-structure数量就是C( cnt[i], 2)。 所有边C( cnt[i], 2)的加和就是答案。

AC代码:

#include <iostream>
#include <cstring>
using namespace std;
const int N = 3e5+5,M=6e5+6;
typedef long long ll;
typedef pair<ll,ll> PII;
ll h[N],ne[M],idx,cnt[M];
PII e[M];

void add(ll a,ll b){
	e[idx]={b,idx},ne[idx]=h[a],h[a]=idx++;	
}

ll du[N];
ll vis[N];
ll X[M],Y[M];
//记录输入的一条边上的两个点 
ll now[N];
int main(){
	ll n,m;
	while(scanf("%lld%lld",&n,&m)!=EOF){
		memset(cnt,0,sizeof cnt);
		memset(du,0,sizeof du);
		memset(vis,0,sizeof vis);
		idx=0;
		for(ll i=0;i<m;i++){
			scanf("%lld%lld",&X[i],&Y[i]);
			du[X[i]]++;
			du[Y[i]]++;		
		}
		//建立有向边的要求: 
		//数小的往度数大的走
		//度数相同则按编号(值)小到大走
		memset(h,-1,sizeof h); 
		for(ll i=0;i<m;i++){
		//在此,将所有的边建立成有向边的形式 
			if(du[X[i]]<du[Y[i]]){
				add(X[i],Y[i]);
			}else if(du[Y[i]]<du[X[i]]){
				add(Y[i],X[i]);
			}else{//编号 
				if(X[i]<Y[i]){
					add(X[i],Y[i]);
				} else{
					add(Y[i],X[i]);
				}
			}
		}
		
		for(ll i=0;i<m;i++){
			ll u=X[i],v=Y[i];//此时直线两端的两个点
			for(ll j=h[u];j!=-1;j=ne[j]){
				ll x=e[j].first;//u所连接的点进行标记 
				vis[x]=i+1;
				now[x]=j;
			} 
			for(ll j=h[v];j!=-1;j=ne[j]){
				ll x=e[j].first;//v所连接的点 
				if(vis[x]==i+1){//如果发现这个点被标记过了,则表示形成了三元环 
					cnt[i]++;
					cnt[now[x]]++;
					cnt[j]++;
				}
			}
		}
		ll ans=0;
		for(ll i=0;i<m;i++){
			ans+=(cnt[i]*(cnt[i]-1))/2;
		}
		cout<<ans<<endl;
	}

	return 0;
} 

祝各位努力早日成为图论dalao!

posted @ 2021-05-30 11:29  ACHanHan  阅读(183)  评论(0编辑  收藏  举报