Codeforces 662C Binary Table(快速沃尔什变换)

Problem

  • 给定一个n(≤20)*m(≤100 000)的01矩阵,每次操作可以将一行或一列取反。
  • 求最终1的最少个数。

    Solution

  • 前置技能:快速沃尔什变换(FWT)
  • 观察到n较小,考虑\(O(2^n)\)枚举每一行选或不选。
  • 不妨设f(x)表示行的操作状态为x时(我们可用一个二进制数表示状态),经过各种列操作后所得到的最少的1的个数。
  • 可以\(O(m)\)再扫一遍所有列。但显然T飞了。

  • 定义\(C_j\)表示有多少列的状态为j;\(E_k\)表示对于某一列而言,若它经过各种行操作状态变成了k,则它再经历各种列操作后最少能得到的1的个数。
  • 显然,\(C_j\)我们对于每一列统计一下即可;而\(E_k\)也很好求,设状态k中有cnt个1,则\(E_k=min(cnt,n-cnt)\)(不进行/进行列操作)。
  • 而且我们也可以得到一个较为显然的式子:\(f(x)=\sum_{x \oplus j=k} C_j*E_k\)。这个式子的思路就是对于所有状态为j的列,我们都通过状态为x的行操作令其变成了\(x\oplus j=k\),然后再看看变成了k以后的答案。
  • 可以暴力枚举j,暴力转移。但是这样的复杂度是\(O(2^{2n})\)的。

  • 注意到xor的特殊性:对于任何\(x\oplus y=z\),有\(y\oplus z=x\)。
  • 因此,上式可化为:\(f(x)=\sum_{j\oplus k=x} C_j*E_k\)。
  • 观察到这是一个卷积的形式,我们用FWT优化它。

  • 时间复杂度:\(O(nm+2^nn)\)。

    Code

#include <bits/stdc++.h>
#define go(i,a,b) for(i=a;i<b;i++)
using namespace std;
typedef long long ll;

const int N=21,M=1e5+1,S=1<<21;
int i,j,n,m,s,tmp;
char str[M];
bool a[N][M];
ll c[S],e[S],f[S],ans;

void FWT(ll *tf)
{
    for(int d=1;d<s;d<<=1)
        for(int m=d<<1,i=0;i<s;i+=m)
            for(int j=0;j<d;j++)
            {
                ll x=tf[i+j],y=tf[i+j+d];
                tf[i+j]=x+y; tf[i+j+d]=x-y;
            }
}  

void UFWT()
{
    for(int d=1;d<s;d<<=1)
        for(int m=d<<1,i=0;i<s;i+=m)
            for(int j=0;j<d;j++)
            {
                ll x=f[i+j],y=f[i+j+d];
                f[i+j]=x+y>>1; f[i+j+d]=x-y>>1;
            }
}

int main()
{
    scanf("%d%d",&n,&m);
    go(i,0,n)
    {
        scanf("%s",str);
        go(j,0,m) a[i][j]=str[j]-48;
    }

    go(i,0,m)
    {
        s=0;
        go(j,0,n) s+=a[j][i]*(1<<j);
        c[s]++;
    }

    s=1<<n;
    go(i,0,s)
    {
        for(tmp=i; tmp; tmp>>=1) e[i]+=tmp&1;
        e[i]=min(e[i],n-e[i]);
    }

    FWT(c); FWT(e);
    go(i,0,s) f[i]=c[i]*e[i];
    UFWT();

    ans=n*m;
    go(i,0,s) ans=min(ans,f[i]);
    printf("%lld",ans);
}

原文地址:https://www.cnblogs.com/Iking123/p/9495603.html

时间: 2024-10-07 05:07:21

Codeforces 662C Binary Table(快速沃尔什变换)的相关文章

[Codeforces]663E Binary Table

某变换好题.不过听说还有O(2^n*n^2)DP的…… Description 给定一个n*m的01矩阵,你可以选择对任意行和任意列取反,使得最终“1”的数量尽量少. Input 第一行两个整数n,m. 接下来n行,每行m个字符,描述一个01矩阵. Output 一个整数表示最少的1的数量. Sample Input 3 4 0110 1010 0111 Sample Output 2 HINT 1 <= n <= 20,1 <= m <= 100000. Solution 首先发

Codeforces 662C(快速沃尔什变换 FWT)

感觉快速沃尔什变换和快速傅里叶变换有很大的区别啊orz 不是很明白为什么位运算也可以叫做卷积(或许不应该叫卷积吧) 我是看 http://blog.csdn.net/liangzhaoyang1/article/details/52819835 里的快速沃尔什变换 这里说一下自己的理解吧,快速傅里叶变换是计算卷积的,就是∑f(x)*g(n-x)这种 快速沃尔什变换也是计算∑f(x)*g(y) ,但这里是计算所有的满足x^y = n(卷积是计算x+y=n)的和 当然,异或也可以换成&,|这些运算符

Fast Walsh-Hadamard Transform——快速沃尔什变换

模板题: 给定$n = 2^k$和两个序列$A_{0..n-1}$, $B_{0..n-1}$,求 $$C_i = \sum_{j \oplus k = i} A_j B_k$$ 其中$\oplus$是某一满足交换律的位运算,要求复杂度$O(nlogn)$. 快速沃尔什变换: 这是什么东西?有用吗?请参阅SDOI2017r2d1-cut. 看到这个大家是不是立刻想到了快速傅里叶变换? $$C_i = \sum_{j + k = i} A_j B_k$$ 我们来想想离散傅里叶变换的本质. $$\b

Codeforces 338D GCD Table 中国剩余定理

题目链接:点击打开链接 给定n*m的矩阵,[i,j]的点值为gcd(i,j) 给定一个k长的序列,问是否能匹配上 矩阵的某一行的连续k个元素 思路: 我们要求出一个解(i,j) 使得 i<=n && j<=m 此时输出 YES 对于j j % b[0] = 0 j+1 % b[1] = 0 ··· j+l % b[l] = 0 根据定理:若 a == b (mod n) => (a+c) == b+c (mod n) 所以将上式变换为 j % b[0] = 0 j % b

【CF662C】Binary Table 按位处理

[CF662C]Binary Table 题意:给你一个$n\times m$的01网格,你可以进行任意次操作,每次操作是将一行或一列的数都取反,问你最多可以得到多少个1? $n\le 20,m\le 10^5$ 题解:我也不知道叫啥了,说状压也不对,说fwt也不太对,就叫按位处理得了. 显然有$O(2^nm)$暴力,先枚举每行是否取反,然后枚举每列,如果0多就取反,否则不取. 但我们发现我们完全可以将本质相同的列一起处理,什么叫本质相同的列呢?假如我们对每行是否取反的状态为S,则所有$xor

FWT快速沃尔什变换学习笔记

FWT快速沃尔什变换学习笔记 1.FWT用来干啥啊 回忆一下多项式的卷积\(C_k=\sum_{i+j=k}A_i*B_j\) 我们可以用\(FFT\)来做. 甚至在一些特殊情况下,我们\(C_k=\sum_{i*j=k}A_i*B_j\)也能做(SDOI2015 序列统计). 但是,如果我们把操作符换一下呢? 比如这样? \(C_k=\sum_{i|j=k}A_i*B_j\) \(C_k=\sum_{i\&j=k}A_i*B_j\) \(C_k=\sum_{i\wedge j=k}A_i*B_

【CF662C】Binary Table(FWT)

[CF662C]Binary Table(FWT) 题面 洛谷 CF 翻译: 有一个\(n*m\)的表格(\(n<=20,m<=10^5\)), 每个表格里面有一个\(0/1\), 每次可以将一行或者一列的\(01\)全部翻转 回答表格中最少有多少个\(1\) 题解 发现\(n\)很小,\(m\)很大 状压是跑不掉了 如果我们确定翻转哪些行,那么答案唯一确定(贪心的选每一列中\(0/1\)的较小值) 相同的列显然可以合并, 把每一列按照\(01\)状压,记\(a[i]\)为状态为\(i\)的列

快速沃尔什变换

快速沃尔什变换 题目背景 模板题,无背景 题目描述 给定长度为\(2^n\)两个序列\(A,B\),设\(C_i=\sum_{j\oplus k=i}A_jB_k\) 分别当\(\oplus\)是or,and,xor时求出\(C\) 输入输出格式 输入格式: 第一行一个数\(n\). 第二行\(2^n\)个数\(A_0..A_{2^n-1}\) 第三行\(2^n\)个数\(B_0..B_{2^n-1}\) 输出格式: 三行每行\(2^n\)个数,分别代表\(\oplus\)是or,and,xor

「CF662C」 Binary Table

「CF662C」 Binary Table 题目链接 题目所给的 \(n\) 很小,于是我们可以考虑这样一种朴素做法:暴力枚举第 \(i\) 行是否翻转,这样每一行的状态就确定了,这时取每一列 \(0/1\) 个数较小的数字即可(因为每一列也可以翻转).这样的时间复杂度是 \(O(m\cdot2^n)\). 但是显然这样过不了. 我们发现表格的具体行列对我们的答案是没有影响的.即我们只需要知道状态为 \(x\) 的行或者状态为 \(x\) 的列的个数即可.由于 \(n\le20\),这启发我们对