刷题总结——小c找朋友(bzoj4264 集合hash)

题目:

Description

幼儿园里有N个小C,两个小C之间可能是朋友也可能不是。所有小C之间的朋友关系构成了一个无向图,这个无向图中有M条边。
园长ATM发现对于两个(不同的)小Ci和j,如果其他的所有小C要么同时是i,j的朋友,要么同时不是i,j朋友的话,这两个小C就很有可能一起去吃饭,成为一对好*友。出于一些未知的原因,ATM需要你帮他求出可能成为好*友的小C的对数。

Input

第一行一个数N,M,如题目描述。
接下来M行,每行2个数表示一条无向边。

Output

输出可能成为好*友的小C的对数。

Sample Input

3 3
1 2
2 3
1 3

Sample Output

3

HINT

N,M<=1000000

题解:

  典型的集合hash题··相当于找两点的边集是否相等····

  先给每个点附上一个unsigned long long范围的随机值···然后如果b与a相连那么hash[a]^rand[b]且hash[b]^rand[a],然后分两点间是否相连两种状况比较即可····

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<cctype>
using namespace std;
const int N=1e6+5;
unsigned long long rando[N],hash[N];
int n,m;
long long ans=0;
struct node
{
  int a,b;
}edge[N];
inline int R()
{
  char c;int f=0;
  for(c=getchar();c<'0'||c>'9';c=getchar());
  for(;c<='9'&&c>='0';c=getchar())  f=(f<<3)+(f<<1)+c-'0';
  return f;
}
int main()
{
  srand(1);
  n=R(),m=R();
  for(int i=1;i<=n;i++)  rando[i]=(unsigned long long)rand()*rand()*rand()*rand();
  for(int i=1;i<=m;i++)
  {
    edge[i].a=R(),edge[i].b=R();
    hash[edge[i].a]^=rando[edge[i].b];hash[edge[i].b]^=rando[edge[i].a];
  }
  for(int i=1;i<=m;i++)
    if((hash[edge[i].a]^rando[edge[i].b])==(hash[edge[i].b]^rando[edge[i].a]))  ans++;
  sort(hash+1,hash+n+1);
  long long cnt=0;
  for(int i=1;i<=n;i++)
  {
    cnt++;
    if(i==n||hash[i]!=hash[i+1])
      ans+=cnt*(cnt-1)/2,cnt=0;
  }
  cout<<ans<<endl;
  return 0;
}
原文地址:https://www.cnblogs.com/AseanA/p/7677235.html