快速沃爾什變換(FWT)及K進制異或卷積&快速子集變換(FST)講解

前言:

  $FWT$是用來處理位運算(異或、與、或)卷積的一種變換。位運算卷積是什麼?形如$f[i]=\sum\limits_{j\oplus k==i}^{ }g[j]*h[k]$的卷積形式(其中$\oplus$爲位運算)就是位運算卷積。若是暴力枚舉的話,時間複雜度是$O(n^2)$,但運用$FWT$來解決就可達到$O(nlog_{n})$的時間複雜度。$FST$則是藉助$FWT$來進行的對子集卷積的優化,至關於$FWT$的一個應用。函數

FWT

與卷積

對於與運算,有一個結論:$(i\&j)\&k==k<-->(i\&k==k)\&\&(j\&k==k)$。優化

那麼咱們就能夠構造一個求父集和的函數$F(i)=\sum\limits_{j\&i==i}^{ }f(i)$,由此咱們能夠推出:spa

$G(k)*H(k)$blog

$=\sum\limits_{i\&k==k}^{ }g(i)\sum\limits_{j\&k==k}^{ }h(j)$it

$=\sum\limits_{(i\&j)\&k==k}^{ }g(i)*h(j)$class

$=\sum\limits_{t\&k==k}^{ }\sum\limits_{i\&j==t}^{ }g(i)*h(j)$擴展

由於$f(t)=\sum\limits_{i\&j==t}^{ }g(i)*h(j)$,二進制

全部上式$=\sum\limits_{t\&k==k}^{ }f(t)=F(k)$im

 所以咱們只須要將$g,h$正變換成$G,H$,而後對應位相乘獲得$F$後再將$F$逆變換回去便可獲得$f$。集合

那麼如何正變換?

如下面爲例:

00 a

01 b

10 c

11 d

咱們從最低位開始考慮,每次只考慮只有當前位不一樣的兩個數之間的影響。顯然求父集只有$1$會對$0$有貢獻,所以咱們將$01$的值加到$00$上,將$11$的值加到$10$上,再看下一位,一樣將$11$的值加到$01$上,將$10$的值加到$00$上。這樣最後$00$的值爲$a+b+c+d$,$01$的值爲$b+d$,$10$的值爲$c+d$,$11$的值爲$d$。一樣逆變換就是將$1$的值從$0$上減掉便可。

附上代碼

void fwt_and(int *a,int opt)
{
    for(int k=2;k<=n;k<<=1)
    {
        for(int i=0,t=k>>1;i<n;i+=k)
        {
            for(int j=i;j<i+t;j++)
            {
                if(opt==1)
                {
                    a[j]=(a[j]+a[j+t])%mod;
                }
                else
                {
                    a[j]=(a[j]-a[j+t]+mod)%mod;
                }
            }
        }
    }
}

或卷積

或卷積和與卷積相似,對於或卷積一樣有結論:$(i|j)|k==k<-->(i|k==k)\&\&(j|k==k)$

此次咱們須要構造一個求子集和的函數$G(i)=\sum\limits_{j|i==i}^{ }g(j)$,推導過程和與卷積相似。

對於正變換,顯然只有$0$對$1$有貢獻;對於逆變換,只須要將$0$的值從$1$中減掉便可。

附上代碼

void fwt_or(int *a,int opt)
{
    for(int k=2;k<=n;k<<=1)
    {
        for(int i=0,t=k>>1;i<n;i+=k)
        {
            for(int j=i;j<i+t;j++)
            {
                if(opt==1)
                {
                    a[j+t]=(a[j+t]+a[j])%mod;
                }
                else
                {
                    a[j+t]=(a[j+t]-a[j]+mod)%mod;
                }
            }
        }
    }
}

異或卷積

對於異或卷積,咱們設$bit(i)$表明$i$的二進制中$1$的奇偶性,所以有一個結論(這裏異或用$\oplus$表示):$bit(i\&k)\oplus bit(j\&k)=bit((i\oplus j)\&k)$

對於原多項式$g$構造$G(i)=\sum\limits_{j=0}^{2^n-1}(-1)^{bit(j\&i)}g(j)$

開始推導:

$G(k)*H(k)$

$=\sum (-1)^{bit(i\&k)}g(i)\sum (-1)^{bit(j\&k)}h(j)$

$=\sum (-1)^{bit((i\oplus j)\& k)}g(i)*h(j)$

$=\sum (-1)^{bit(t\&k)}\sum\limits_{i\oplus j==t}^{ }g(i)*h(j)$

$=\sum (-1)^{bit(t\&k)}f(t)$

$=F(k)$

對於正變換,咱們一樣從最低位向最高位考慮,每次只考慮只有當前位不一樣的兩個數之間的影響。對於每對$0$和$1$(設值分別爲$a$和$b$),$0\&0=0$和$0\&1=0$都不會影響$bit$的值,因此$0$那個位置的值變成$a+b$;$1\&0=0$不會影響$bit$的值,但$1\&1=1$會影響$bit$的值(至關於在前面乘上一個$-1$的係數),所以$1$那個位置的值變成$a-b$。對於逆變換,至關於咱們如今知道兩個位置$x=a+b$,$y=a-b$,求$a$和$b$,能夠獲得$a=\frac{x+y}{2},b=\frac{x-y}{2}$。

附上代碼

void fwt_xor(int *a,int opt)
{
    int tmp;
    for(int k=2;k<=n;k<<=1)
    {
        for(int i=0,t=k>>1;i<n;i+=k)
        {
            for(int j=i;j<i+t;j++)
            {
                tmp=a[j];
                a[j]=(a[j]+a[j+t])%mod;
                a[j+t]=(tmp-a[j+t]+mod)%mod;
                if(opt==-1)
                {
                    a[j]=1ll*a[j]*inv%mod;
                    a[j+t]=1ll*a[j+t]*inv%mod;
                }
            }
        }
    }
}

K進制異或卷積

能夠發現二進制的異或運算至關於不進位加法即每一位對應相加後對$2$取模,而與運算至關於不進位乘法即每一位對應相乘後對$2$取模,$bit$至關於求二進制每一位的和對$2$取模。那麼咱們將這些在二進制下的運算擴展到$K$進制能夠發現一樣知足上述的結論,但對於從$g$求$G$的部分每一個數前面的係數的底數是$-1$,顯然這個係數不能擴展到$K$進制。那麼咱們如今就須要找一個係數$w$知足$w^0,w^1,w^2……w^{k-1}$都各不相同且$w^i=w^{i\%k}$。從$FFT$中咱們知道了複數單位根這個東西,那麼咱們徹底能夠將$w$取$K$次單位根,這樣就能夠知足以上性質了!類比二進制亦或的正變換也能夠得出$K$進制的正變換。

 FST

$FST$一般用來優化一類子集$DP$,例如$f(S)=\sum g(T)*h(S-T)$,其中$T$是$S$的子集。

對於這個方程咱們不能直接用$FWT$卷積,由於若是$g(i)*h(j)$會對$f(k)$有貢獻就要求$i|j=k,i\&j=0$。

顯然位運算卷積不能同時知足這兩個要求,那麼咱們將方程變成二維表示$f[S][i]=\sum\limits_{j=1}^{i}\sum\limits_{a|b==S}^{ }g[a][j]*h[b][i-j]$

其中的第二維表示集合大小,對於$f[S][i]$只有當$i=|S|$時纔有值,這樣第一維保證並集爲$S$,第二維保證交集爲空集,因此其餘不合法的狀態不會影響答案。

這樣咱們就能對式子進行卷積:$F[S][i]=\sum\limits_{j=1}^{i}G[S][j]*H[S][i-j]$。求出每一個$F$的值再$FWT$逆變換回去便可。

原題中的$f[S]$就是二維狀態中的$f[S][|S|]$。

時間複雜度爲$O(2^n*n^2)$。

相關文章
相關標籤/搜索