題意:有m個問題,n份長度爲m的答卷,每一個答案都是A或者Bc++
如今要任選一個問題的子集,若是大於等於k對答卷在該子集上答案有不一樣,就說這個子集是可辯別的git
求可辯別的子集數量數組
首先將答卷轉爲01串,那麼兩個答卷相同就等價於兩個01串異或爲0spa
對於一個問題的子集S,先考慮計算異或等於S的01串對數量,即code
咱們用一個桶數組num[S]表示等於S的01串數量,那麼F能夠寫爲對象
這是FWT的標準形式,能夠在\(\rm O(m2^m)\)的時間求出F數組get
咱們實際要求的集合T, 只要和S有交,就能夠被F(S)貢獻it
於是有class
簡單容斥一下,有im
這裏的\(S\cap T=\emptyset\) 等價於 \(S\subseteq U-T\),枚舉子集\(3^m\)轉移不可取
實際上這是在CF上考屢次的SOS模型,能夠用DP在\(\rm O(m2^m)\)作
設dp[S][i]表示作完前i位,S的答案,轉移考慮第i位是0仍是1
若是第i位是0,直接從前面轉移過來,即dp[S][i]=dp[S][i-1]
若是第i位是1,那麼考慮這位能夠爲0或1,即dp[S][i]=dp[S][i-1]+dp[S^(1<<i)][i-1]
實際中第二維能夠被壓掉,由於從小往大枚舉S時,轉移的對象老是前面的
這樣就作完了,\(ans=\sum\limits_{S}[n*n-2G(s)\geq2k]\)
#include<bits/stdc++.h> #define int long long using namespace std; int rd(){ int ret=0,f=1;char c; while(c=getchar(),!isdigit(c))f=c=='-'?-1:1; while(isdigit(c))ret=ret*10+c-'0',c=getchar(); return ret*f; } // const int MOD = 998244353,INV2=499122177; const double Cor[2][2]={{1,0},{1,1}}, Cand[2][2]={{1,1},{0,1}}, Cxor[2][2]={{1,1},{1,-1}}, ICor[2][2]={{1,0},{-1,1}}, ICand[2][2]={{1,-1},{0,1}}, ICxor[2][2]={{0.5,0.5},{0.5,-0.5}}; void FWT(double *f,const double c[2][2],int n){ for(int len=1;len<n;len<<=1) for(int p=0;p<n;p+=len+len) for(int i=p;i<p+len;i++){ double sav=f[i]; f[i]=(c[0][0]*f[i]+c[0][1]*f[i+len]); f[i+len]=(c[1][0]*sav+c[1][1]*f[i+len]); } } void bitmul(double *f,double *g,const double c[2][2],const double ic[2][2],int n){ FWT(f,c,n);//FWT(g,c,n); for(int i=0;i<n;i++) f[i]*=f[i]; FWT(f,ic,n); } const int MAXN = 2200006; int n,m,k; double f[MAXN]; char s[50]; signed main(){ n=rd();m=rd();k=rd(); for(int i=1;i<=n;i++){ scanf("%s",s+1); int tmp=0; for(int j=1;j<=m;j++){ if(s[m-j+1]=='A') tmp|=(1<<(j-1)); } f[tmp]+=1.0; } bitmul(f,f,Cxor,ICxor,1<<m); for(int i = 0; i < m; i++) for(int j = 0; j < (1<<m); j++) if(j & (1 << i)) f[j] += f[j ^ (1 << i)]; int ans=0; for(int i=0;i<(1<<m);i++){ if(n*n-(long long)(f[i]+0.5)>=2*k) ans++; } cout<<ans; return 0; }