FWT 總結

FWT

快速沃爾什變換,簡寫FWT,變換肯定會想到FFT,和其相似,FWT同樣是用於求解某些特定的卷積的。
FFT求解的問題一般可以化成如下形式
Ck=Σi+j=kAiBjC_k=\Sigma_{i+j=k}A_i*B_j
但是會發現,如果求解的CkC_kkk的條件變成ij=ki|j=k , i&ji \And j 或者iji \bigoplus j就沒有辦法計算,所以我們需要使用FWT來解決這類問題。
關於FWT的證明就不寫了,我們可以通過其結論可知

oror的FWT滿足

FWT(A)=(FWT(A0),FWT(A0+A1)) FWT(A)=(FWT(A_0),FWT(A_0+A_1))
中間的A0,A1A_0,A_1分別表示AA的前2n12^{n-1}和後2n12^{n-1}部分,其實這個結論也可以通過感性認識一下,因爲進行的是或操作,顯然如果將A0A_0中的某一項和A1A_1中的某一項進行計算,得到的結果只能夠影響到FWT(A1)FWT(A_1)的部分,對於FWT(A0)FWT(A_0)沒有貢獻,所以可以得到這個結論。
而根據
FWT(A+B)=FWT(A)FWT(B)FWT(A+B)=FWT(A)*FWT(B)
我們就可以得到
FWT(AB)=FWT(A)FWT(B)FWT(A|B)=FWT(A)*FWT(B)的結論,也就證明了FWT的正確性。

andand的FWT滿足

FWT(A)=(FWT(A0+A1),FWT(A1)) FWT(A)=(FWT(A_0+A_1),FWT(A_1))

xorxor的FWT滿足

FWT(A)=(FWT(A0+A1),FWT(A0A1)) FWT(A)=(FWT(A_0+A_1),FWT(A_0-A_1))
這兩個部分同樣可以經過操作證明FWT的正確性。

void FWT_or(int *a,int opt)
{
    for(int i=1;i<N;i<<=1)
        for(int p=i<<1,j=0;j<N;j+=p)
            for(int k=0;k<i;++k)
                if(opt==1)a[i+j+k]=(a[j+k]+a[i+j+k])%MOD;
                else a[i+j+k]=(a[i+j+k]+MOD-a[j+k])%MOD;
}
void FWT_and(int *a,int opt)
{
    for(int i=1;i<N;i<<=1)
        for(int p=i<<1,j=0;j<N;j+=p)
            for(int k=0;k<i;++k)
                if(opt==1)a[j+k]=(a[j+k]+a[i+j+k])%MOD;
                else a[j+k]=(a[j+k]+MOD-a[i+j+k])%MOD;
}
void FWT_xor(int *a,int opt)
{
    for(int i=1;i<N;i<<=1)
        for(int p=i<<1,j=0;j<N;j+=p)
            for(int k=0;k<i;++k)
            {
                int X=a[j+k],Y=a[i+j+k];
                a[j+k]=(X+Y)%MOD;a[i+j+k]=(X+MOD-Y)%MOD;
                if(opt==-1)a[j+k]=1ll*a[j+k]*inv2%MOD,a[i+j+k]=1ll*a[i+j+k]*inv2%MOD;
            }
}

代碼和思路來源於https://www.cnblogs.com/cjyyb/p/9065615.html

題目總結

HDU5909 Tree Cutting

簡單來講就是給你一棵樹,每個點有權值ViV_i,定義樹上聯通塊的權值Sk=i=1..size(k)ViS_k = \bigoplus_{i=1..size(k)}V_i,求問有多少個聯通塊的權值爲ii
首先不難看出這個問題是一道樹形dp,記f[i][j]f[i][j]表示以ii爲根的子樹中,聯通塊值爲jj的數目,如果直接暴力合併子樹的話,複雜度爲O(N3)O(N^3),如果在合併異或的時候,我們使用FWT,複雜度就會降成O(N2log2N)O(N^2log_{2}N)

#include <bits/stdc++.h>
#define rep( i , l , r ) for( int i = (l) ; i <= (r) ; ++i )
#define per( i , r , l ) for( int i = (r) ; i >= (l) ; --i )
#define erep( i , u ) for( int i = head[(u)] ; ~i ; i = e[i].nxt )
using namespace std;
const int maxn = 1111 , maxm = 1111 , MOD = 1e9 + 7 , inv2 = 5e8 + 4;
int head[maxn] , _t = 0;
struct edge{
	int v , nxt;
}e[maxn << 1];
inline void addedge( int u , int v ){
	e[_t].v = v , e[_t].nxt = head[u] , head[u] = _t++;
	e[_t].v = u , e[_t].nxt = head[v] , head[v] = _t++;
}

int f[maxn][maxm] , ans[maxm];

void FWT( int *a , int N , int inv ){
	for( int i = 1 ; i < N ; i <<= 1 )
		for( int p = i << 1 , j = 0 ; j < N ; j += p )
			for( int k = 0 ; k < i ; ++k ){
				int X = a[j + k] , Y = a[i + j + k];
				
				a[j + k] = (X + Y) % MOD;
				a[i + j + k] = (X + MOD - Y) % MOD;
				if( inv == -1 ){
					a[j + k] = 1ll * a[j + k] * inv2 % MOD;
					a[i + j + k] = 1ll * a[i + j + k] * inv2 % MOD;
				}
			}
}
int N , M;
void dfs( int u , int fa ){
	FWT( f[u] , M , 1 );
	erep( i , u ){
		int v = e[i].v;
		if( v == fa ) continue;
		dfs( v , u );
		for( int i = 0 ; i < M ; ++i ) f[u][i] = (f[u][i] * f[v][i]) % MOD;
	}
	FWT( f[u] , M , -1 );
	if( ++f[u][0] == MOD ) f[u][0] -= MOD;
	FWT( f[u] , M , 1 );
}
inline int _read(){
	int x = 0 , f = 1;
	char ch = getchar();
	while( ch > '9' || ch < '0' ){
		if( ch == '-' ) f = -1;
		ch = getchar();
	}
	while( '0' <= ch && ch <= '9' ){
		x = (x << 3) + (x << 1) + ch - '0';
		ch = getchar();
	}
	return x * f;
}

int main(){
	int T = _read();
	while( T-- ){
		N = _read() , M = _read();
		memset( f , 0 , sizeof f );
		memset( head , 0xff , sizeof head );
		memset( ans , 0 , sizeof ans );
		rep( i , 1 , N ) f[i][_read()] = 1;
		_t = 0;
		int u , v;
		rep( i , 1 , N - 1 ){
			u = _read() , v = _read();
			addedge( u , v );
		}
		dfs( 1 , -1 );
		for( int i = 1 ; i <= N ; ++i ) FWT( f[i] , M , -1 );
		for( int i = 1 ; i <= N ; ++i )
			if( --f[i][0] < 0 ) f[i][0] += MOD;
		
		for( int i = 1 ; i <= N ; ++i ){
			for( int j = 0 ; j < M ; ++j ){
				ans[j] = (ans[j] + f[i][j]) % MOD;
			}
		}
		
		for( int i = 0 ; i < M ; ++i ){
			printf("%d",ans[i]) , putchar(i==M-1?'\n':' ');
		}
	}
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章