题解
假设选出题目的集合为 $S$ ,考虑求出它的数对数。
$(i,j)$ 如果不相同,则 $a_i \text{xor} a_j \& S=0$ 。
因此我们先用 $\text{fwt}$ 求出异或值为 $T$ 的数对数,然后对于 $S$ 来说,如果 $T$ 上的值能贡献答案,说明 $S \&T>0$ 。
考虑容斥,减去 $S\&T=0$ 的数对数即可。
也就是说, $T$ 的补集包含了 $S$ 。
因此考虑 $\text{sosdp}$ , $f_{i,j}$ 表示 $x\&i=x$ 并且 $x \text{xor} i<2^j$的 $x$ 的答案的和。
考虑转移,如果 $i$ 的第 $j$ 位为 $0$ ,那么 $f_{i,j}=f_{i,j-1}$ ,否则 $f_{i,j}=f_{i,j-1}+f_{i \text{xor} 2^j,j-1}$ 。
效率: $O(m2^m)$ 。
代码
#include<bits/stdc++.h> #define LL long long using namespace std; const int N=1<<20; int n,m,a[N],t,ans; LL Z,A[N<<1],B[N<<1],f[N]; char ch[25]; void Fwt(LL *g,int o){ for (int i=1;i<t;i<<=1) for (int j=0;j<t;j+=(i<<1)) for (int k=0;k<i;k++){ LL x=g[j+k],y=g[i+j+k]; g[j+k]=x+y;g[i+j+k]=x-y; if (!o) g[j+k]/=2,g[i+j+k]/=2; } } int main(){ cin>>n>>m>>Z; for (int i=1;i<=n;i++){ scanf("%s",ch); for (int j=0;j<m;j++) a[i]=(a[i]<<1)|(ch[j]=='A'); A[a[i]]++;B[a[i]]++; } t=1<<m+1;Fwt(A,1);Fwt(B,1); for (int i=0;i<t;i++) A[i]*=B[i]; Fwt(A,0);t>>=1; for (int i=0;i<t;i++) f[i]=A[i]; for (int i=0;i<m;i++) for (int j=0;j<t;j++) if (j&(1<<i)) f[j]+=f[j^(1<<i)]; for (int i=0;i<t;i++){ LL v=1ll*n*n-f[(t-1)^i]; if (v>=Z+Z) ans++; } cout<<ans<<endl;return 0; }