BJOJ 4264 小C找朋友
幼儿园里有N个小C,两个小C之间可能是朋友也可能不是。所有小C之间的朋友关系构成了一个无向图,这个无向图中有M条边。 园长ATM发现对于两个(不同的)小Ci和j,如果其他的所有小C要么同时是i,j的朋友,要么同时不是i,j朋友的话,这两个小C就很有可能一起去吃饭,成为一对好友。出于一些未知的原因,ATM需要你帮他求出可能成为好友的小C的对数。
题目翻译:给你一个n个点m条边的无向图,要求你计算出边集(邻居集)相同的点对数
Solution:
震惊!Hash还有此等妙用
我们对每一个点随机赋一个非零权值,然后再开一个数组维护邻居集的Hash
每次加边都将两个端点的邻居集合XOR上对方的权值
然后将这样得出的邻居集合拿去统计一遍答案(sort之后统计Hash值相同的点的个数,每个Hash相同的部分的贡献为(cnt-1)*cnt/2)
然后再将邻居集合XOR上本身的权值然后再统计一遍答案,然后这题就做完了
Code:
#include<bits/stdc++.h> const int N=1e6+5; using namespace std; typedef unsigned long long ull; int n,m; ull a[N],b[N]; ull aa[N],bb[N]; ull ans; struct Node{ ull w,ww; bool operator <(const Node &k)const{ return w<k.w; } bool operator ==(const Node &k)const{ return w==k.w&&ww==k.ww; } }q[N]; void work() { cin>>n>>m; a[0]=7758258; aa[0]=7355608; for(int i=1;i<=n;i++) { a[i]=a[i-1]*131; aa[i]=aa[i-1]*998244353; } for(int i=1,x,y;i<=m;i++) { scanf("%d%d",&x,&y); b[x]^=a[y];bb[x]^=aa[y]; b[y]^=a[x];bb[y]^=aa[x]; } for(int i=1;i<=n;i++) { q[i]=(Node){b[i],bb[i]}; } sort(q+1,q+1+n); int i=1,last=1; for(i=1,last=1;i<=n;i++) { if(!(q[i]==q[last])) { ull cnt=i-last; ans+=(cnt-1)*(cnt)/2; last=i; } } ull cnt=n-last+1; ans+=(cnt-1)*(cnt)/2; for(int i=1;i<=n;i++) { q[i]=(Node){b[i]^a[i],bb[i]^aa[i]}; } sort(q+1,q+1+n); for(i=1,last=1;i<=n;i++) { if(!(q[i]==q[last])) { ull cnt=i-last; ans+=(cnt-1)*(cnt)/2; last=i; } } cnt=n-last+1; ans+=(cnt-1)*(cnt)/2; printf("%lld",ans); } int main() { //freopen("friend.in","r",stdin); //freopen("friend.out","w",stdout); srand(time(0)); work(); return 0; }