[BZOJ3198][SDOI2013]Spring(容斥+Hash)
给定n个六元数,问有多少对数有m元对应相等。
考虑“有多少对数至少m元对应相等”的求法,显然枚举相等的位置,在这些位置上Hash统计即可。
容斥定理:至少有k个的-C(k+1,k)* 至少有k+1个的+C(k+2,k) *至少有k+2个的…=恰好有k个的。
按上式容斥,问题得解。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=100010,mod1=131,mod2=1e6+33; 9 int n,m,cnt,a[N][7],C[7][7],h[mod2+10],to[N],nxt[N]; 10 ll ans,sm[N]; 11 12 bool chk(int x,int y,int k){ 13 rep(i,1,6) if (k&(1<<(i-1)) && a[x][i]!=a[y][i]) return 0; 14 return 1; 15 } 16 17 ll calc(int S){ 18 ll res=0; cnt=0; 19 memset(h,0,sizeof(h)); 20 rep(i,1,n){ 21 ll x=0; int p=0; 22 rep(j,1,6) if (S&(1<<(j-1))) x=(x*mod1+a[i][j])%mod2; 23 for (p=h[x]; p; p=nxt[p]) 24 if (chk(to[p],i,S)) { res+=sm[p]; sm[p]++; break; } 25 if (!p) to[++cnt]=i,sm[cnt]=1,nxt[cnt]=h[x],h[x]=cnt; 26 } 27 return res; 28 } 29 30 int main(){ 31 freopen("bzoj3198.in","r",stdin); 32 freopen("bzoj3198.out","w",stdout); 33 scanf("%d%d",&n,&m); 34 rep(i,1,n) rep(j,1,6) scanf("%d",&a[i][j]); 35 C[0][0]=1; rep(i,1,6){ C[i][0]=1; rep(j,1,i) C[i][j]=C[i-1][j-1]+C[i-1][j]; } 36 rep(S,0,63){ 37 int cnt=0; 38 rep(i,0,5) if (S&(1<<i)) cnt++; 39 if (cnt<m) continue; 40 ll t=calc(S)*C[cnt][m]; 41 ans+=((cnt-m)&1) ? -t : t; 42 } 43 printf("%lld\n",ans); 44 return 0; 45 }