1. 程式人生 > >【CF662C】Binary Table 按位處理

【CF662C】Binary Table 按位處理

urn strong class i++ xor ios ++ limit mes

【CF662C】Binary Table

題意:給你一個$n\times m$的01網格,你可以進行任意次操作,每次操作是將一行或一列的數都取反,問你最多可以得到多少個1?

$n\le 20,m\le 10^5$

題解:我也不知道叫啥了,說狀壓也不對,說fwt也不太對,就叫按位處理得了。

顯然有$O(2^nm)$暴力,先枚舉每行是否取反,然後枚舉每列,如果0多就取反,否則不取。

但我們發現我們完全可以將本質相同的列一起處理,什麽叫本質相同的列呢?假如我們對每行是否取反的狀態為S,則所有$xor S$中1的個數相同的列我們都認為是相同的。那麽現在問題就變成了對於所有S,$xor S$中1的個數為i的列的個數是多少。我們可以設f[S][i]表示這個狀態,初始時f[T][0]++(T是某一列的狀態)。然後我們枚舉二進制的每一位是否取反,再從大到小枚舉1的個數,進行轉移即可。最後的答案就是$max\{\sum\limits_{i=0}^n f[S][i]\times max\{i,n-i\}\}$。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int n,m,ans;
char s[21][100001];
int f[21][(1<<20)+1];
int main()
{
	scanf("%d%d",&n,&m),ans=1<<30;
	int i,j,k;
	for(i=0;i<n;i++)	scanf("%s",s[i]);
	for(i=0;i<m;i++)
	{
		int tmp=0;
		for(j=0;j<n;j++)	if(s[j][i]==‘1‘)	tmp|=1<<j;
		f[0][tmp]++;
	}
	for(i=0;i<n;i++)	for(j=n;j;j--)	for(k=0;k<(1<<n);k++)	f[j][k]+=f[j-1][k^(1<<i)];
	for(k=0;k<(1<<n);k++)
	{
		int tmp=0;
		for(i=0;i<=n;i++)	tmp+=min(i,n-i)*f[i][k];
		ans=min(ans,tmp);
	}
	printf("%d",ans);
	return 0;
}

【CF662C】Binary Table 按位處理