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 @ 2024-10-05 16:56  liuboom  阅读(16)  评论(0)    收藏  举报