BZOJ 3198 Sdoi2013 spring Hash+容斥原理

题目大意:给定n个元素,每个元素是一个六元组,求有多少对元素满足相同的位置恰好有k个

首先对于恰好有K个这种东西果断考虑容斥原理

我们2^6枚举相同的位置

恰好有k个元素相同的对数=至少有k个位置相同的对数-至少有k+1个位置相同的对数+至少有k+2个位置相同的对数……

但是我们计数时会发现一些问题 比如下面这组样例显然是0:

2 3

1 2 3 4 5 5

1 2 3 4 6 6

但是这一对元素被加了C(4,3)次,只被减掉了C(4,4)次

因此我们将公式改成这样:

恰好有k个元素相同的对数=至少有k个位置相同的对数*C(k,k)-至少有k+1个位置相同的对数*C(k+1,k)+至少有k+2个位置相同的对数*C(k+2,k)……

这样就行了

至于统计同一组位置上有多少对相同的元素,可以利用哈希表

首先将这最多6个数利用RK的思想Hash成一个数,然后插入Hash表即可

时间复杂度O(n*2^6)

如果将哈希表改成排序,时间复杂度O(nlogn*2^6),会TLE

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 100100
#define BASE 999911657
using namespace std;
const int C[7][7]={
	{1,0,0,0,0,0,0},
	{1,1,0,0,0,0,0},
	{1,2,1,0,0,0,0},
	{1,3,3,1,0,0,0},
	{1,4,6,4,1,0,0},
	{1,5,10,10,5,1,0},
	{1,6,15,20,15,6,1},
};
int n,k;
int a[M][6];
int digit[64];
long long ans;
namespace Hash_Set{
	struct List{
		List *next;
		unsigned long long hash;
		int val;
		List() {}
		List(List *_,unsigned long long __):
			next(_),hash(__),val(0) {}
	}*head[1001001],mempool[M],*C=mempool;
	int tim[1001001],T;
	inline void Initialize()
	{
		++T;C=mempool;
	}
	int& Hash(unsigned long long hash)
	{
		int pos=hash%1001001;
		if(tim[pos]!=T)
			tim[pos]=T,head[pos]=0x0;
		for(List *temp=head[pos];temp;temp=temp->next)
			if(temp->hash==hash)
				return temp->val;
		head[pos]=new (C++) List(head[pos],hash);
		return head[pos]->val;
	}
}
long long Calculate(int state)
{
	using namespace Hash_Set;
	int i,j;
	long long re=0;
	Initialize();
	for(i=1;i<=n;i++)
	{
		unsigned long long hash=0;
		for(j=1;j<=6;j++)
			if( state&(1<<j-1) )
				(hash*=BASE)+=a[i][j];
		int &val=Hash(hash);
		re+=(val++);
	}
	return re;
}
int main()
{
	int i,j;
	cin>>n>>k;
	for(i=1;i<=n;i++)
		for(j=1;j<=6;j++)
			scanf("%d",&a[i][j]);
	for(i=0;i<64;i++)
	{
		digit[i]=digit[i>>1]+(i&1);
		if(digit[i]>=k)
			ans+=(digit[i]-k&1?-1:1)*Calculate(i)*C[digit[i]][k];
	}
	cout<<ans<<endl;
	return 0;
}


你可能感兴趣的:(hash,容斥原理,bzoj,BZOJ3198)