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

 

posted @ 2018-10-17 18:27  HocRiser  阅读(242)  评论(0编辑  收藏  举报