题目描述:
题目大意:给定一个无向图,问图中共边三元环有多少对,点数n<=100000,m<=200000,多组输入输出。
例图:
样例输入:
2
4 5
1 2
2 3
3 4
4 1
1 3
4 6
1 2
2 3
3 4
4 1
1 3
2 4
样例输出:
1
6
题目分析:
这道题朴素的想法是枚举每条边,然后如果边的两个端点的连变数大于等于3,就再计算这两个点所连的共同的点个数为cnt,它们两两都可构成共边三元环,于是ans+=cnt*(cnt-1)/2。时间复杂度 O(m*n)。
同理如果直接枚举点,时间复杂度 O( n 3 n^3 n3)。
**正解:**对于每个点,可以先把与它相连的点打上标记,再枚举所有与它相连且连边数比它小的点的边集来求每条边的贡献,可以证明这样的复杂度是O(m m \sqrt{m} m)的。实际上避免了重复计算(如|菊|花|图)。
时间复杂度证明:
考虑按每个点的连边数是否大于 m \sqrt{m} m分为重点和轻点。故所有边可分为三种:
1.轻点连轻点:有O(m)条边,但每个轻点的连的边集数最多只有O( m \sqrt{m} m)(定义),总复杂度为O(m m \sqrt{m} m)。
2.重点连轻点:有O(m)条边,根据做法只枚举轻点的边集,同样只有O( m \sqrt{m} m),总复杂度为O(m m \sqrt{m} m)。
3.重点连重点:一个重点最多连O(m)条边,最多有O(2 m \sqrt{m} m)个重点(因为算连边数时,每条边被算了两次),且这两种情况是不可能同时存在的,总复杂度也应为O(m m \sqrt{m} m)。
PS: 考虑为什么以 m \sqrt{m} m来划分,设划分的连边数为d,则时间复杂度为O( m ∗ d + m ∗ m d m*d+m*\frac{m}{d} m∗d+m∗dm),均值不等式, m \sqrt{m} m划分最小。
附代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=1e5+10;
const int M=2e5+10;
int n,m,tot,t,nxt[M*2],to[M*2],first[N],du[N],vis[N];
long long ans;
struct node{
int x;
int y;
}bian[M];
inline int readint()
{
char ch;int i=0,f=1;
for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
if(ch=='-') {ch=getchar();f=-1;}
for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<3)+(i<<1)+ch-'0';
return i*f;
}
inline void create(int x,int y)
{
tot++;
nxt[tot]=first[x];
first[x]=tot;
to[tot]=y;
}
int main()
{
//freopen("star.in","r",stdin);
//freopen("star.out","w",stdout);
int x,y;
t=readint();
while(t--)
{
tot=0;ans=0;
memset(first,0,sizeof(first));
memset(du,0,sizeof(du));
memset(vis,0,sizeof(vis));
n=readint();m=readint();
for(register int i=1;i<=m;i++)
{
x=readint();y=readint();
create(x,y);
create(y,x);
du[x]++;du[y]++;
}
for(register int i=1;i<=n;i++)
{
for(register int e=first[i];e;e=nxt[e]) vis[to[e]]=i;//打标记
for(register int e=first[i];e;e=nxt[e])
{
int v=to[e];
if(du[v]<=du[i])
{
if(du[i]==du[v]&&v<=i) continue;//相等特判,v<=i说明前面已算过
int cnt=0;
for(register int p=first[v];p;p=nxt[p])//实际上这里应该用hash来O(1)判,但直接暴力判也过得了
if(vis[to[p]]==i) cnt++;
ans+=(long long)cnt*(cnt-1)/2;
}
}
}
printf("%I64d\n",ans);
}
return 0;
}