[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\)

然而可以发现

\(i\oplus j=k\) 可以变成 \(i\oplus 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 = '\n') {
  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 ;
}
posted @ 2019-12-11 11:22  _Isaunoya  阅读(171)  评论(0编辑  收藏  举报