刷题总结——小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
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; }