题目大意:给定一张无向图,求有多少对点 (x,y)(x≠y) 满足对于任意一点 z(z≠x,z≠y) 满足边 (x,z) 和边 (y,z) 要么都存在,要么都不存在
对于一个点
x
我们搞出与这个点相邻的点的集合
那么如果
(x,y)
之间没有边那么点对
(x,y)
满足条件等价于
Adj(x)=Adj(y)
如果
(x,y)
之间有边那么点对
(x,y)
满足条件等价于
Adj(x)−y=Adj(y)−x
怎么表示一个集合呢?我们给一个点赋予一个64位的整数,然后一个集合用集合内所有的点的异或值来代替就行了
然后一个集合就变成了一个数,没有边的排序处理,有边的枚举边就行了
时间复杂度
O(nlogn+m)
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 1001001
using namespace std;
struct edge{
int x,y;
}edges[M];
int n,m;
long long a[M],b[M],ans;
int main()
{
srand(19980402);
int i,x,y;
cin>>n>>m;
for(i=1;i<=n;i++)
a[i]=(long long)rand()*rand()+rand();
for(i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
edges[i].x=x;
edges[i].y=y;
b[x]^=a[y];
b[y]^=a[x];
}
for(i=1;i<=m;i++)
{
x=edges[i].x;
y=edges[i].y;
if( (b[x]^a[y])==(b[y]^a[x]) )
++ans;
}
sort(b+1,b+n+1);
int cnt=0;
for(i=1;i<=n;i++)
{
++cnt;
if(i==n||b[i]!=b[i+1])
ans+=(long long)cnt*(cnt-1)>>1,cnt=0;
}
cout<<ans<<endl;
return 0;
}