zoukankan      html  css  js  c++  java
  • 【题解】 神仙的游戏 FWT

    Legend

    ...

    (校内 OJ 题目不公开)

    Link ( extrm{to HOJ})

    Editorial

    (n) 特别小,考虑对每一列状压为 (n) 位二进制数。

    对于每一行,考虑是否翻转, 则对于每一列可以贪心。但这样做复杂度太高。有没有办法可以一次性求出所有情况的答案?

    答案是有,设 (H(i)) 为行翻转状态为 (i) 的答案,(F(i)) 为最初的列为 (i) 的列有多少个,(G(i)) 表示状态为 (i) 的列翻或不翻的最小代价,则:

    [egin{aligned} H(k)&=sum_{i=0}^{2^n-1}F(i)G(ioplus k)\ &=sum_{i=0}^{2^n-1}sum_{j=0}^{2^n-1}[ioplus k=j]F(i)G(j)\ &=sum_{i=0}^{2^n-1}sum_{j=0}^{2^n-1}[ioplus j=k]F(i)G(j) end{aligned} ]

    你惊人地发现这就是个异或 FWT!

    时间复杂度 (O(nm+n2^n))

    Code

    #include <bits/stdc++.h>
    
    #define debug(...) ;//fprintf(stderr ,__VA_ARGS__)
    #define __FILE(x)
    	freopen(#x".in" ,"r" ,stdin);
    	freopen(#x".out" ,"w" ,stdout)
    #define LL long long
    
    const int MX = 1 << 20;
    const LL MOD = 1e9 + 7;
    const LL inv = (MOD + 1) / 2;
    
    int read(){
    	char k = getchar(); int x = 0;
    	while(k < '0' || k > '9') k = getchar();
    	while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar();
    	return x;
    }
    
    void chkmin(int &a ,int b){a = std::min(a ,b);}
    int bitcnt(int a){
    	int x = 0;
    	while(a) a -= a & -a ,++x;
    	return x;
    }
    
    char str[20][MX];
    int n ,m ,cnt[MX];
    
    int G[MX] ,F[MX];
    void FWT(int *A ,int limit ,int type){
    	for(int mid = 1 ; mid < limit ; mid <<= 1)
    		for(int j = 0 ; j < limit ; j += mid << 1)
    			for(int k = 0 ; k < mid ; ++k){
    				LL x = A[j + k] ,y = A[j + k + mid];
    				A[j + k] = (x + y) % MOD;
    				A[j + k + mid] = (x - y + MOD) % MOD;
    				if(type == -1){
    					A[j + k] = A[j + k] * inv % MOD;
    					A[j + k + mid] = A[j + k + mid] * inv % MOD;
    				}
    			}
    }
    
    int main(){
    	__FILE(game);
    	n = read() ,m = read();
    	for(int i = 0 ; i < n ; ++i) scanf("%s" ,str[i]);
    	for(int j = 0 ; j < m ; ++j){
    		int a = 0;
    		for(int i = 0 ; i < n ; ++i){
    			a |= (str[i][j] == '1') << i;
    		}
    		++F[a];
    	}
    	for(int i = 0 ; i < (1 << n) ; ++i){
    		int c = bitcnt(i);
    		G[i] = std::min(c ,n - c);
    	}
    
    	int limit = 1 << n;
    	FWT(F ,limit ,1) ,FWT(G ,limit ,1);
    	for(int i = 0 ; i < (1 << n) ; ++i) F[i] = 1LL * F[i] * G[i] % MOD;
    	FWT(F ,limit ,-1);
    	
    	int ans = MX;
    	for(int i = 0 ; i < (1 << n) ; ++i)
    		ans = std::min(ans ,F[i]);
    	printf("%d
    " ,ans);	
    	return 0;
    }
    
  • 相关阅读:
    C#基于引用创建单链表
    锻炼自己的思维模式
    [数据结构]C#基于数组实现泛型顺序表
    DEV Express
    [LeetCode] Combinations (bfs bad、dfs 递归 accept)
    [LeetCode] Wildcard Matching
    [LeetCode] Remove Duplicates from Sorted List II
    [LeetCode] Partition List
    [LeetCode] Scramble String(树的问题最易用递归)
    [LeetCode] Decode Ways(DP)
  • 原文地址:https://www.cnblogs.com/imakf/p/14471316.html
Copyright © 2011-2022 走看看