動作状態を保持片考える\(I \)を最終的な状態ならば、\(J \) 、および提供される\(a_iを\)のような初期状態を表す(I \)\列の数であり、\(b_j \)終了状態のためのものである\ (J \)カラムの寄与が提供されてもよい(逆反転か、前処理)、答えは\(\ A_ {I SUM \ XOR \ J} b_j \) 、配列に設定されている\(C_I = \ sum_ { J \ XOR \ I} = K a_k b_j \) 、または異なる標準畳み込み、FWT缶のサブセット。
畳み込み配列は、タイトルを二回開くことを忘れないでください
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define N (1<<n)
int n,m,s[25][100005],a[2000005],b[2000005];
char ch[25][100005];
void FWT_xor(int *a,int opt){
for(int i=1;i<N;i<<=1)
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
{
int X=a[j+k],Y=a[i+j+k];
a[j+k]=(X+Y);a[i+j+k]=(X-Y);
if(opt==-1)a[j+k]=1ll*a[j+k]/2,a[i+j+k]=1ll*a[i+j+k]/2;
}
}
signed main() {
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++) {
scanf("%s",ch[i]+1);
for(int j=1;j<=m;j++) s[i][j]=ch[i][j]-'0';
}
for(int j=1;j<=m;j++) {
int x=0;
for(int i=1;i<=n;i++) {
x=x*2+s[i][j];
}
a[x]++;
}
for(int i=0;i<1<<n;i++) {
int t=__builtin_popcount(i);
b[i]=min(n-t,t);
}
FWT_xor(a,1);
FWT_xor(b,1);
for(int i=0;i<1<<n;i++) a[i]*=b[i];
FWT_xor(a,-1);
int ans = a[0];
for(int i=1;i<1<<n;i++) ans=min(ans,a[i]);
cout<<ans<<endl;
}