CF662C Binary Table

LINK:CF662C Binary Table

一个nm的表格 每个元素都是0/1 每次操作可以选择一行或一列 将0/1翻转.可以操作无限次。

问最终局面最少有多少个1.\(n\leq 20,m\leq 100000\)

可以发现 先翻列再翻行等价于先翻行再翻列 先翻行再翻列再翻行 如果行是相同的 等价于翻列 反之同上一种情况。

对于任意一对行列之间的关系只有上述的几种情况 故可以发现 最优操作可以转换成 先翻行再翻列。

之所以这样是发现了行数较少 暴力枚举行的状态。

此时只有列能翻了 每一列都是独立的 所以此时找到每一列最多的某个数字即可。

复杂度 \(2^n\cdot n\cdot m\)通过不了。

可以发现 预处理一下每一列的状态 然后和枚举的状态异或一下就能得到新一列的状态 直接预处理某个状态下的1的个数即可。

那么就变成了\(2^n\cdot m\)

发现每一列的状态有可能会重复。

设 a[i]表示 列的状态为i出现的次数 b[i]表示0/1的最小值。

可以发现当状态为 w时此时全局的答案为 \(\sum_{i=0}^{2^n-1}a[i]\cdot b[i,xor,w]\)

复杂度好像更高了 = =.

不过没关系 我们想要求出来对于所有w的答案.设f[w]为行翻转情况为w时的答案。

\(f[w]=\sum_{i=0}^{2^n-1}a[i]\cdot b[i,xor,w]\)

变换一下形式\(f[w]=\sum_{ixorj=w}a[i]\cdot b[j]\)

惊喜的发现这竟然是一个异或卷积。预处理b数组和a数组之后FWT即可。真的妙。

const int MAXN=100010;
int n,m,maxx;
ll a[1<<21],b[1<<21],w[1<<21];
char c[21][MAXN];
inline void FWT_xor(ll *a,int op)
{
	for(int len=1;len<=maxx+1;len=len<<1)
	{
		int mid=len>>1;
		for(int j=0;j<=maxx;j+=len)
			for(int i=0;i<mid;++i)
			{
				ll x=a[i+j],y=a[i+j+mid];
				if(op==1)
				{
					a[i+j]=x+y;
					a[i+j+mid]=x-y;
				}
				else 
				{
					a[i+j]=(x+y)>>1;
					a[i+j+mid]=(x-y)>>1;
				}
			}
	}
}
int main()
{
	freopen("1.in","r",stdin);
	gt(n);gt(m);
	rep(1,n,i)gc(c[i]);
	rep(1,m,j)
	{
		int s=0;
		rep(1,n,i)if(c[i][j]=='1')s=s|(1<<(i-1));
		++a[s];
	}
	maxx=(1<<n)-1;
	rep(1,maxx,i)w[i]=w[i>>1]+(i&1);
	rep(0,maxx,i)b[i]=min(w[i],n-w[i]);
	FWT_xor(a,1);FWT_xor(b,1);
	rep(0,maxx,i)a[i]=a[i]*b[i];
	FWT_xor(a,-1);ll ans=INF;
	rep(0,maxx,i)ans=min(ans,a[i]);
	putl(ans);return 0;
}

注意xor 的FWT为 a0=a0+a1 a1=a0-a1
IFWT为 a0=(a0+a1)>>1;a1=(a0-a1)>>1;

posted @ 2020-04-01 22:01  chdy  阅读(139)  评论(0编辑  收藏  举报