快速沃爾什變換(FWT)

問題

FWT用來解決這樣的一個問題:
給出多項式A(x),B(x),求C(x),其中
C(k)=ij=kA(i)B(j)
(也可以換成and/or)
要求在O(nlogn) 的複雜度內求出C(x)

定義

定義tf(A) 操作表示(A是一個長度爲2的冪的多項式,k表示A的長度,A0A1分別表示A的左半邊和右半邊)

tf(A)={Ak=1(tf(A0)+tf(A1),tf(A0)tf(A1))else
其中”,”表示物理順序拼接

性質

然後有一條性質tf(A)+tf(B)=tf(A+B)
證明略,可以用數學歸納法來證

因爲C=A⊕B,所以可以得出C=(A0B0+A1B1,A0B1+A1B0)
等於把異或第一位爲0的數加到左邊,異或第一位爲1的數加到右邊

然後xjb證出tf(A)tf(B)=tf(C) (當C=A⊕B時)
所以可以先求出tf(A)和tf(B),之後相乘求逆

逆運算

定義tf(A)的逆運算utf(A),表示utf(tf(A))=A

utf(A)={Ak=1(utf(A0)+utf(A1)2,utf(A0)utf(A1)2)else
證明還是數學歸納法

所以這樣對tf(C)做utf操作,就可以得出C了
而tf和utf操作都是O(nlogn)

其它

關於三種運算的tf和utf
這裏寫圖片描述
其中異或的utf是可以拆的,也就是說utf(A)+utf(B)=utf(A+B)
證明和tf基本一樣(就是多了個/2)

例題

codeforces662CBinary Table 二進制表
C. Binary Table
time limit per test6 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
You are given a table consisting of n rows and m columns. Each cell of the table contains either 0 or 1. In one move, you are allowed to pick any row or any column and invert all values, that is, replace 0 by 1 and vice versa.

What is the minimum number of cells with value 1 you can get after applying some number of operations?

Input
The first line of the input contains two integers n and m (1 ≤ n ≤ 20, 1 ≤ m ≤ 100 000) — the number of rows and the number of columns, respectively.

Then n lines follows with the descriptions of the rows. Each line has length m and contains only digits ‘0’ and ‘1’.

Output
Output a single integer — the minimum possible number of ones you can get after applying some sequence of operations.

Example
inputCopy
3 4
0110
1010
0111
outputCopy
2
C.二進制表
每次測試的時間限制6秒
每次測試的內存限制256兆字節
輸入標準輸入
產量標準輸出
您將獲得一個由n行和m列組成的表。表的每個單元格包含0或1。在一個步驟中,您可以選擇任何行或任何列並反轉所有值,即將0替換爲1,反之亦然。

在應用一些操作後,您可以獲得值爲1的最小單元格數是多少?

輸入
輸入的第一行包含兩個整數Ñ和米(1≤  Ñ  ≤20,1≤  米  ≤100 000) -行的,分別的數量和列的數量。

然後n行跟隨行的描述。每行的長度爲m,僅包含數字“ 0 ”和“ 1 ”。

產量
輸出一個整數 - 應用一系列操作後可以獲得的最小數量。


輸入複製
3 4
0110
1010
0111
產量複製
2

題解

就是給出一個01矩陣,每次可以把一行或一列取反
求所有可能的矩陣中最小的1個數

因爲n很小,所以考慮狀壓n
如果行的方案確定了,那麼答案就是每一列異或行後取反與否的最大值之和


設f(s)表示當行選取的狀態爲s時的答案
設ans(s)表示某一列狀態爲s時的答案
要考慮整列取反的情況
設g(s)表示初始列狀態爲s的列數

f(s)=ss=kans(k)g(s)
等於是枚舉每一種可能的列,然後進行列的取反,答案就是所有列之和
可以變成
f(s)=s=ksans(k)g(s)
就是FWT的形式了

code

// codeforces 662C Binary Table
// start:25/08/17
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a<b?a:b)
using namespace std;

typedef long long AA[1048576];
int A[100001];
AA s,a,b,c;
int n,m,i,j,k,l,Len;
char ch;
long long ans;

void tf(long long *a)
{
    int i,j,k,l;
    int S=1,s1=1,s2=Len+1;

    fo(i,1,n)
    {
        S<<=1;
        s2>>=1;

        fo(j,0,s2-1)
        {
            fo(k,0,s1-1)
            {
                l=j*S+k;
                s[l]=a[l]+a[l+s1];
                s[l+s1]=a[l]-a[l+s1];
            }
        }

        memcpy(a,s,sizeof(long long)*(Len+1));

        s1<<=1;
    }
}

void utf(long long *a)
{
    int i,j,k,l;
    int S=1,s1=1,s2=Len+1;

    fo(i,1,n)
    {
        S<<=1;
        s2>>=1;

        fo(j,0,s2-1)
        {
            fo(k,0,s1-1)
            {
                l=j*S+k;
                s[l]=(a[l]+a[l+s1])>>1;
                s[l+s1]=(a[l]-a[l+s1])>>1;
            }
        }

        memcpy(a,s,sizeof(long long)*(Len+1));

        s1<<=1;
    }
}

int main()
{
    scanf("%d%d",&n,&m); Len=pow(2,n)-1;
    fo(i,1,n)
    {
        scanf("\n");
        fo(j,1,m)
        {
            scanf("%c",&ch);
            A[j]=(A[j]<<1)+(ch-'0');
        }
    }

    fo(i,1,m)
    b[A[i]]++;
    fo(i,0,Len)
    {
        j=i;
        k=0;
        while (j)
        {
            k+=j&1;
            j>>=1;
        }
        a[i]=min(k,n-k);
    }

    tf(a);
    tf(b);

    fo(i,0,Len)
    c[i]=a[i]*b[i];

    utf(c);

    ans=233333333;
    fo(i,0,Len)
    ans=min(ans,c[i]);

    printf("%I64d\n",ans);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章