【BZOJ4264】小C找朋友 随机化

【BZOJ4264】小C找朋友

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

题解:我们为每一个点赋一个随机权值,然后令s[x]表示所有与x相邻的点的权值的异或和,然后只需要统计出哪些点的s值相同即可。(当然,你也可以采用hash,它们的本质思想是相同的。)

但是你会发现样例很良心的为你指出了一种特殊情况,x和y可以相邻。那么将每个点的s异或上自己的权值再统计一遍就行了,容易发现这两种情况并不会导致重复计算。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=1000010;
int n,m;
ll v[maxn],s[maxn],p[maxn],ans,sum;
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
	return ret*f;
}
int main()
{
	srand(2333666);
	n=rd(),m=rd();
	int i,a,b;
	for(i=1;i<=n;i++)	v[i]=(ll)rand()*rand()*rand()*rand();
	for(i=1;i<=m;i++)
	{
		a=rd(),b=rd();
		s[a]^=v[b],s[b]^=v[a];
	}
	for(i=1;i<=n;i++)	p[i]=s[i];
	sort(p+1,p+n+1);
	for(sum=0,i=1;i<=n;i++)
	{
		if(p[i]!=p[i-1])	ans+=sum*(sum-1)/2,sum=0;
		sum++;
	}
	ans+=sum*(sum-1)/2;
	for(i=1;i<=n;i++)	p[i]=s[i]^v[i];
	sort(p+1,p+n+1);
	for(sum=0,i=1;i<=n;i++)
	{
		if(p[i]!=p[i-1])	ans+=sum*(sum-1)/2,sum=0;
		sum++;
	}
	ans+=sum*(sum-1)/2;
	printf("%lld",ans);
	return 0;
}
posted @ 2017-08-23 20:40  CQzhangyu  阅读(315)  评论(0编辑  收藏  举报