• [CF662C Binary Table][状压+FWT]


    CF662C Binary Table

    一道 FWT 的板子…比较难想就是了

    有一个 (n)(m) 列的表格,每个元素都是 (0/1),每次操作可以选择一行或一列,把 (0/1) 翻转,即把 (0) 换为 (1) ,把 (1) 换为 (0) 。请问经过若干次操作后,表格中最少有多少个 (1)

    (1 leq n leq 20)
    (1 leq m leq 10^5)

    先说说 FWT 干嘛的吧

    (F_k = sum_{i oplus j=k} a_i * b_j)

    首先呢 这题其实是有个暴力做法的

    (认为是 n 行 m 列的一个矩阵)
    复杂度大概是 (2^n * m)

    就是你暴力搞 (n) 枚举每个状态复杂度自然是 (2^n) 的 然后你每次搜索/状压 搞到一个地方之后 算当前列的 0/1 个数取 (min) 因为列是可以翻转的…
    暴力做法 没了 但是这种做法在CF里并不给分所以没啥用

    但是对以下的做题有大用处

    你可以把最开始矩阵 (m) 列状压 (n) 这样就成了个二进制

    然后 (a) 数组计数
    (b_i) 数组表示 i 的 0的个数,1的个数取 min
    然后如果对矩阵变换也可以表示成状态 那么就是最开始的状态 (i oplus k)

    然而可以发现

    (ioplus j=k) 可以变成 (ioplus k=j)

    然后 FWT 还是可以用的

    直接跑板子
    因为你最开始变换的是 (k) 最后要枚举取个 (min) 求最优解
    这题没了…

    #include <bits/stdc++.h>
    #define int long long
    #define rep(a , b , c) for(int a = b ; a <= c ; ++ a)
    #define Rep(a , b , c) for(int a = b ; a >= c ; -- a)
    #define go(u) for(int i = G.head[u] , v = G.to[i] , w = G.dis[i] ; i ; v = G.to[i = G.nxt[i]] , w = G.dis[i])
    
    using namespace std ;
    using ll = long long ;
    using pii = pair < int , int > ;
    using vi = vector < int > ;
    
    int read() {
      int x = 0 ; bool f = 1 ; char c = getchar() ;
      while(c < 48 || c > 57) { if(c == '-') f = 0 ; c = getchar() ; }
      while(c > 47 && c < 58) { x = (x << 1) + (x << 3) + (c & 15) ; c = getchar() ; }
      return f ? x : -x ;
    }
    
    template <class T> void print(T x , char c = '
    ') {
      static char st[100] ; int stp = 0 ;
      if(! x) { putchar('0') ; }
      if(x < 0) { x = -x ; putchar('-') ; }
      while(x) { st[++ stp] = x % 10 ^ 48 ; x /= 10 ; }
      while(stp) { putchar(st[stp --]) ; } putchar(c) ;
    }
    
    template <class T> void cmax(T & x , T y) { x < y ? x = y : 0 ; }
    template <class T> void cmin(T & x , T y) { x > y ? x = y : 0 ; }
    
    const int _N = 1e6 + 10 ;
    struct Group {
      int head[_N] , nxt[_N << 1] , to[_N] , dis[_N] , cnt = 1 ;
      Group () { memset(head , 0 , sizeof(head)) ; }
      void add(int u , int v , int w = 1) { nxt[++ cnt] = head[u] ; to[cnt] = v ; dis[cnt] = w ; head[u] = cnt ; }
    } ;
    
    const int N = 1 << 21 ;
    typedef int arr[N] ;
    
    int n , m ;
    void FWT(int * a) {
    	for(int d = 1 ; d <= n - 1 ; d <<= 1) {
    		for(int i = 0 ; i <= n - 1 ; i += (d << 1))
    			rep(j , 0 , d - 1) {
    				int x = a[i + j] , y = a[i + j + d] ;
    				a[i + j] = x + y ;
    				a[i + j + d] = x - y ;
    			}
    	}
    }
    void IFWT(int * a) {
    	for(int d = 1 ; d <= n - 1 ; d <<= 1) {
    		for(int i = 0 ; i <= n - 1 ; i += (d << 1))
    			rep(j , 0 , d - 1) {
    				int x = a[i + j] , y = a[i + j + d] ;
    				a[i + j] = x + y >> 1 ;
    				a[i + j + d] = x - y >> 1 ;
    			}
    	}
    }
    int digit() {
    	char c = getchar() ;
    	while(! (c >= 48 && c <= 57)) c = getchar() ;
    	if(c == 49) return 1 ;
    	return 0 ;
    }
    arr a , b , f , g , cnt ;
    signed main() {
    	n = read() ; m = read() ;
    	rep(i , 0 , n - 1) {
    		rep(j , 0 , m - 1) {
    			if(digit()) 
    				g[j] |= (1 << i) ;
    		}
    	}
    	rep(i , 0 , m - 1) a[g[i]] ++ ;
    	int nn = n ;
    	n = 1 << n ;
    	rep(i , 1 , n - 1) cnt[i] = cnt[i >> 1] + (i & 1) ;
    	rep(i , 0 , n - 1) b[i] = min(cnt[i] , nn - cnt[i]) ;
    	FWT(a) ; FWT(b) ;
    	rep(i , 0 , n - 1) a[i] *= b[i] ;
    	IFWT(a) ;
    	ll ans = 1e18 ;
    	rep(i , 0 , n - 1) ans = min(ans , a[i]) ;
    	print(ans) ;
    	return 0 ;
    }
    
  • 相关阅读:
    javascript Date类的扩展
    软件工程师好了歌 (转)
    您可能不知道的.Net2.0小技巧
    您未必知道的Js技巧
    复活吧,架构师!
    技巧系列文章
    不要使用paddingtop控制内容开始的位置
    JQuery Offset实验与应用(转载)
    2008最佳Windows应用程序
    精选15个国外CSS框架
  • 原文地址:https://www.cnblogs.com/Isaunoya/p/12021450.html
Copyright © 2020-2023  润新知