abc396_g & CF662C 题解
abc396_g & CF662C 题解
做 abc 做到了原题。
考虑暴力,我们枚举每一列是否翻转,然后每一行的贡献就是 \(0,1\) 个数的最小值,时间复杂度 \(O(n2^m)\)。
我们把上述东西写成式子,设 \(a_i\) 为行的初始状态为 \(i\) 的行数,\(b_i\) 为 \(i\) 二进制 \(0,1\) 个数的最小值。我们枚举给每一行异或上的是 \(k\),答案为
\[\sum _{i=0} ^{2^m-1} a_i\times b_{i\oplus k}
\]
在这个式子中,相乘的两个数异或恒为 \(k\),于是设 \(f_i\) 为 \(k=i\) 时的答案,有
\[f_i=\sum _{j\oplus k} a_j\times b_k
\]
这是异或卷积的形式,直接用 FWT 做即可,复杂度 \(O(m2^m)\)。
代码如下
const int N=1<<18;
int n,m;
void fwt(int *a) {
	for(int i=1;i<1<<m;i<<=1) {
		for(int j=0;j<1<<m;j+=i<<1) {
			fu(k,j,j+i) {
				int t=a[k+i];
				a[k+i]=(a[k]-a[k+i]);
				a[k]+=t;
			}
		}
	}
}
void ifwt(int *a) {
	for(int i=1;i<1<<m;i<<=1) {
		for(int j=0;j<1<<m;j+=i<<1) {
			fu(k,j,j+i) {
				int t=a[k+i];
				a[k+i]=(a[k]-a[k+i]);
				a[k]+=t;
			}
		}
		fu(j,0,1<<m) a[j]=a[j]/2;
	}
}
int a[N],b[N];
char s[20];
signed main(){
	cin>>n>>m;
	fo(i,1,n) {
		scanf("%s",s+1);
		int t=0;
		fo(j,1,m) if(s[j]=='1') t|=(1<<j-1);
		a[t]++;
	}
	#define popc __builtin_popcount
	fu(i,0,1<<m) b[i]=min(popc(i),popc(i^((1<<m)-1)));
	fwt(a),fwt(b);
	fu(i,0,1<<m) a[i]*=b[i];
	ifwt(a);
	int ans=1e18;
	fu(i,0,1<<m) ans=min(ans,a[i]);
	cout<<ans;
	return 0;
}

                
            
        
浙公网安备 33010602011771号