题目描述
题解
假设选出题目的集合为 S S S ,考虑求出它的数对数。
( i , j ) (i,j) (i,j) 如果不相同,则 a i xor a j & S = 0 a_i \text{xor} a_j \& S=0 aixoraj&S=0 。
因此我们先用 fwt \text{fwt} fwt 求出异或值为 T T T 的数对数,然后对于 S S S 来说,如果 T T T 上的值能贡献答案,说明 S & T > 0 S \&T>0 S&T>0 。
考虑容斥,减去 S & T = 0 S\&T=0 S&T=0 的数对数即可。
也就是说, T T T 的补集包含了 S S S 。
因此考虑 sosdp \text{sosdp} sosdp , f i , j f_{i,j} fi,j 表示 x & i = x x\&i=x x&i=x 并且 x xor i < 2 j x \text{xor} i<2^j xxori<2j的 x x x 的答案的和。
考虑转移,如果 i i i 的第 j j j 位为 0 0 0 ,那么 f i , j = f i , j − 1 f_{i,j}=f_{i,j-1} fi,j=fi,j−1 ,否则 f i , j = f i , j − 1 + f i xor 2 j , j − 1 f_{i,j}=f_{i,j-1}+f_{i \text{xor} 2^j,j-1} fi,j=fi,j−1+fixor2j,j−1 。
效率: O ( m 2 m ) O(m2^m) O(m2m) 。
代码
#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;
}