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; }