CodeForces 663E - Binary Table FWT

来源:互联网 发布:php admin value 编辑:程序博客网 时间:2024/05/17 06:18

【题目链接】见这里
【题意】
给出一个n(n≤20)行m(m≤105)列的01矩阵。每次操作可以将某一行取反或者将某一列取反。要求操作后的矩阵中的1的个数最少,求最小个数。
【解题思路】
由于行比较少,我们考虑状压。状压每一列的状态,sta第i位为1,代表第i行为1,否则为0。现在我们有一个F(sta) 代表状态为sta的时候的列的数量。考虑一下我们操作了哪些行?对于一个状态mask,第i位为一代表我们翻转了第i行,否则代表没翻转。
考虑一个mask对sta的影响,显然是sta ^ mask。不妨设为res。
然后对于一个res,我们不妨预处理一个G(res)代表res这样的一列翻转之后和翻转之前1的个数的最小值。
但是m有10^5直接做是不可能的,我们考虑如何优化?
构造一个函数H(mask),表示操作为mask的时候1的个数。不难想到

H(mask)=F(sta)×G(stamask),sta=(0,2n1)

我们改写一下这个等式:

H(mask)=[stamask=res]F(sta)×G(res),sta,res=(0,2n1)

这样难道不就是做一个异或卷积和就行了吗?所以我们用FWT优化即可。
【AC代码】

////Created by BLUEBUFF 2016/1/10//Copyright (c) 2016 BLUEBUFF.All Rights Reserved//#pragma comment(linker,"/STACK:102400000,102400000")#include <ext/pb_ds/assoc_container.hpp>#include <ext/pb_ds/tree_policy.hpp>#include <ext/pb_ds/hash_policy.hpp>#include <set>#include <map>#include <queue>#include <stack>#include <cmath>#include <cstdio>#include <time.h>#include <cstdlib>#include <cstring>#include <complex>#include <sstream> //isstringstream#include <iostream>#include <algorithm>using namespace std;//using namespace __gnu_pbds;typedef long long LL;typedef pair<int, LL> pp;#define REP1(i, a, b) for(int i = a; i < b; i++)#define REP2(i, a, b) for(int i = a; i <= b; i++)#define REP3(i, a, b) for(int i = a; i >= b; i--)#define CLR(a, b)     memset(a, b, sizeof(a))#define MP(x, y)      make_pair(x,y)template <class T1, class T2>inline void getmax(T1 &a, T2 b) { if (b>a)a = b; }template <class T1, class T2>inline void getmin(T1 &a, T2 b) { if (b<a)a = b; }const int maxn = 20;const int maxm = 1e5+5;const int maxs = 10;const int maxp = 1e3 + 10;const int INF  = 1e9;const int UNF  = -1e9;const int mod  = 1e9 + 7;const int rev = (mod + 1) >> 1; // FWTconst double PI = acos(-1);//headint A[maxm], n , m;char s[maxm];int F[1<<maxn], G[1<<maxn];void FWT(int 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++)            {                int x=a[i+j],y=a[i+j+d];                a[i+j]=(x+y)%mod,a[i+j+d]=(x-y+mod)%mod;                //xor:a[i+j]=x+y,a[i+j+d]=(x-y+mod)%mod;                //and:a[i+j]=x+y;                //or:a[i+j+d]=x+y;            }}void UFWT(int 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++)            {                int x=a[i+j],y=a[i+j+d];                a[i+j]=1LL*(x+y)*rev%mod,a[i+j+d]=(1LL*(x-y)*rev%mod+mod)%mod;                //xor:a[i+j]=(x+y)/2,a[i+j+d]=(x-y)/2;                //and:a[i+j]=x-y;                //or:a[i+j+d]=y-x;            }}void solve(int a[],int b[],int n){    FWT(a,n);    FWT(b,n);    for(int i=0;i<n;i++) a[i]=1LL*a[i]*b[i]%mod;    UFWT(a,n);}int main(){    cin >> n >> m;    for(int i = 0; i < n; i++){        scanf("%s", s);        for(int j = 0; j < m; j++){            A[j] = A[j] << 1 | (s[j] - '0');        }    }    for(int i = 0; i < m; i++) F[A[i]] += 1;    for(int i = 0; i < (1<<n); i++){        int t = __builtin_popcount(i);        G[i] = min(t, n - t);    }    solve(F, G, 1<<n);    int ans = n * m;    for(int i = 0; i < 1 << n; i++){        ans = min(ans , (int) F[i]);    }    cout << ans << endl;}
0 0
原创粉丝点击