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;
原文地址:https://www.cnblogs.com/chdy/p/12616266.html