BZOJ3198 [Sdoi2013]spring 哈希 容斥原理

欢迎访问~原文出处——博客园-zhouzhendong

去博客园看该题解


题目传送门 - BZOJ3198


题意概括

  有n(1<=n<=100000)组数据,每组数据6个数。

  现在问有几对数据,满足其数字相同的个数恰好为k。

  0<=k<=6


题解

  首先暴搜是不行的。

  然后我们发现可以哈希+容斥。

  对于有至少有x个数字相同的情况,我们可以枚举+hash解决(这个很简单,不用说了吧)。

  然后是最关键的。

  假设ans[i]表示至少有i个相同的情况总数。

  那么会有重复:

  比如某4个数相同,那么在其相应的ans[3]中,有一部分值是这4个数的排列。

  比如某两组数据的第1,2,3,4个数字相等,那么对于1,2,3相等,1,2,4相等,1,3,4相等,2,3,4相等都算了一遍,所以ans[3]要减掉ans[4]*C(4,3)

  那么同理,所有的从大到小减一减,然后就得出了答案。


代码

#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;
const int N=100005;
LL MOD=1000000007,MOD2=998244353,Ti=23333,Ti2=65119;
int n,k;
LL a[N][6],ans[6],C[10][10],v[N],v2[N];
int cnt1(int x){
	int ans=0;
	while (x)
		ans+=x&1,x>>=1;
	return ans;
}
LL solve(int k){
	LL ans=0;
	for (int i=0;i<(1<<6);i++){
		if (cnt1(i)!=k)
			continue;
		for (int j=1;j<=n;j++){
			v[j]=v2[j]=0;
			for (int k=0;k<6;k++)
				if (i&(1<<k))
					v[j]=(v[j]*Ti+a[j][k])%MOD,v2[j]=(v2[j]*Ti2+a[j][k])%MOD2;
			v[j]+=v2[j]*2033333333;
		}
		sort(v+1,v+n+1);
		v[n+1]=v[n]+1;
		int prev=1;
		for (int j=2;j<=n+1;j++)
			if (v[j]!=v[j-1])
				ans+=1LL*(j-prev)*(j-prev-1)/2,prev=j;
	}
	return ans;
}
int main(){
	scanf("%d%d",&n,&k);
	for (int i=1;i<=n;i++)
		for (int j=0;j<6;j++)
			scanf("%lld",&a[i][j]);
	for (int i=0;i<=6;i++)
		ans[i]=solve(i);
	memset(C,0,sizeof C);
	for (int i=0;i<=6;i++)
		C[i][0]=1;
	for (int i=1;i<=6;i++)
		for (int j=1;j<=i;j++)
			C[i][j]=C[i-1][j]+C[i-1][j-1];
	for (int i=6;i>=1;i--)
		for (int j=0;j<i;j++)
			ans[j]-=ans[i]*C[i][j];
	printf("%lld\n",ans[k]);
	return 0;
}

  

posted @ 2017-10-31 20:42  zzd233  阅读(301)  评论(0编辑  收藏  举报