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;
}
posted @   liuboom  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示