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

来源:互联网 发布:mysql私房菜深入浅出 编辑:程序博客网 时间:2024/05/18 00:07

大家都很强, 可与之共勉 。

题意:
  给出一个n(n20)m(m105)列的01矩阵。每次操作可以将某一行取反或者将某一列取反。要求操作后的矩阵中的1的个数最少,求最小个数。

题解:
  我们会发现行数很小,是一个可以状态压缩的范围(滑稽)。
  于是我萌就状压行
  对于每一列,用state表示这一列的状态,若第i位为1,则表示那一列的第i行为1
  用A(state)表示state这样的行的个数。

  然后我们考虑如何实现操作。可以确定的一点是操作奇数次相当于操作一次,偶数次相当于不操作。

  我们继续状压操作,令opt为一个操作,第i位为0表示该行不翻转,否则表示翻转。容易知道该列被操作后的结果为optxorstate。我们对于每一个状态都可以预处理出它翻转后的1与不翻转的1的数量多少。对于每一个情形的最优解我们用B(situation)表示。

  于是我们枚举每一种行的操作opt,设该种操作的最优解为C(opt)

  于是可得到C(opt)=2n1state=1A(state)B(statexoropt)

  怎么优化呢?由异或的性质:

  C(opt)=2n1state=12n1res=1[statexorres==opt]A(state)B(res)

  再由异或的性质:
  C(opt)=2n1state=12n1res=1[statexoropt==res]A(state)B(res)

  于是这个东西叫做异或卷积,她可以用FWT解决。

  最后取出C数组的最小值即可。

# include <bits/stdc++.h>struct FastWalshHadamardTransform  {    void dwt ( long long* a, int n )  {        for ( int d = 1 ; d < n ; d <<= 1 )  {            for ( int m = d << 1, i = 0 ; i < n ; i += m )  {                for ( int j = 0 ; j < d ; ++ j )  {                    long long x = a [i + j], y = a [i + j + d] ;                    a [i + j] = x + y, a [i + j + d] = x - y ;                    //and a[i+j] = x+y;                    //or a[i+j+d] = x+y;                }            }        }    }    void idwt ( long long* a, int n )  {        for ( int d = 1 ; d < n ; d <<= 1 )  {            for ( int m = d << 1, i = 0 ; i < n ; i += m )  {                for ( int j = 0 ; j < d ; ++ j )  {                    long long x = a [i + j], y = a [i + j + d] ;                    a [i + j] = ( x + y ) >> 1, a [i + j + d] = ( x - y ) >> 1 ;                    //and a[i+j] = x-y                    //or a[i+j+d] = y-x                }            }        }    }    void main ( long long* a, long long* b, long long* res, int n )  {        dwt ( a, n ) ;        dwt ( b, n ) ;        for ( int i = 0 ; i < n ; ++ i )  res [i] = a [i] * b [i] ;        idwt ( res, n ) ;    }} fwt ;const int L = 1 << 22 ;char mat [22] [100010] ;long long a [L], b [L], c [L] ;int main ( )  {    int n, m ;    scanf ( "%d%d", & n, & m ) ;    for ( int i = 0 ; i < n ; ++ i )  scanf ( "%s", mat [i] ) ;    for ( int i = 0 ; i < m ; ++ i )  {        int state ( 0 ) ;        for ( int j = 0 ; j < n ; ++ j )    state |= ( mat [j] [i] == '1' ) << j ;        ++ a [state] ;    }    for ( int i = 0, lim = 1 << n ; i < lim ; ++ i )  {        b [i] = std :: min ( __builtin_popcount ( i ), n - __builtin_popcount ( i ) ) ;    }    fwt.main ( a, b, c, 1 << n ) ;    std :: cout << *std :: min_element ( c, c + ( 1 << n ) ) << std :: endl ;    return 0 ;}
原创粉丝点击