解题思路
我们考虑给所有的边一个方向。具体的,如果一条边两个端点的度数不一样,则由度数较小的点连向度数较大的点,否则由编号较小的点连向编号较大的点。称小的点优先级比大的点高
即:原图中的环 ( u , v ) , ( v , w ) , ( u , w ) (u,v),(v,w),(u,w) (u,v),(v,w),(u,w)(不妨设u优先级比v高,v优先级比w高)在新图上表现为 ( u − > v , v − > w , u − > w ) (u->v,v->w,u->w) (u−>v,v−>w,u−>w)
手推一下,这样的图是有向无环的。原图中的三元环一定与对应有向图中所有形如 < u → v > , < u → w > , < v → w > <u→v>,<u→w>,<v→w> <u→v>,<u→w>,<v→w>的子图一一对应,我们只需要枚举 u 的出边,再枚举 v 的出边,然后检查 w 是不是 uu 指向的点即可。
代码
#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
int n,m,kk,d[100010],u[200010],v[200010],head[200010];
ll ans;
struct c {
int x,next;
} a[200010];
void add(int x,int y) {
a[++kk].x=y;
a[kk].next=head[x];
head[x]=kk;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u[i],&v[i]);
d[u[i]]++,d[v[i]]++;
}
for(int i=1;i<=m;i++)
{
if((d[u[i]]>d[v[i]])||(d[u[i]]==d[v[i]]&&u[i]>v[i]))
swap(u[i],v[i]);
add(u[i],v[i]);
}
memset(v,0,sizeof(v));
for(int i=1;i<=n;i++)
{
for(int j=head[i];j;j=a[j].next)
v[a[j].x]=1;
for(int j=head[i];j;j=a[j].next)
{
int y=a[j].x;
for(int k=head[y];k;k=a[k].next)
if(v[a[k].x])ans++;
}
for(int j=head[i];j;j=a[j].next)
v[a[j].x]=0;
}
printf("%lld",ans);
}