FWT
快速沃爾什變換,簡寫FWT,變換肯定會想到FFT,和其相似,FWT同樣是用於求解某些特定的卷積的。
FFT求解的問題一般可以化成如下形式
但是會發現,如果求解的中的條件變成 , 或者就沒有辦法計算,所以我們需要使用FWT來解決這類問題。
關於FWT的證明就不寫了,我們可以通過其結論可知
的FWT滿足
中間的分別表示的前和後部分,其實這個結論也可以通過感性認識一下,因爲進行的是或操作,顯然如果將中的某一項和中的某一項進行計算,得到的結果只能夠影響到的部分,對於沒有貢獻,所以可以得到這個結論。
而根據
我們就可以得到
的結論,也就證明了FWT的正確性。
的FWT滿足
的FWT滿足
這兩個部分同樣可以經過操作證明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
簡單來講就是給你一棵樹,每個點有權值,定義樹上聯通塊的權值,求問有多少個聯通塊的權值爲。
首先不難看出這個問題是一道樹形dp,記表示以爲根的子樹中,聯通塊值爲的數目,如果直接暴力合併子樹的話,複雜度爲,如果在合併異或的時候,我們使用FWT,複雜度就會降成。
#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;
}