HDU 5909 Tree Cutting 樹形DP+快速沃爾什變換

題目大意:給出一棵樹,每個點有一個點權,求對於每個i[0,m) 輸出有多少個連通誘導子圖的異或和爲i
n1000m<210

別問我爲什麼隔了這麼久突然跑回來更blog……我只是在填以前剩下的坑而已。。。
(我花了一整個高三去打遊戲,然後花了一整個大一補高三的內容,到了大二,我退學了2333)

FWT

定義:
對於一個長爲n=2k 的數組A ,定義A0 爲這個數組的前2k1 項,A1 爲這個數組的後2k1 項,A=(A0,A1)
A±B={A[0]±B[0],A[1]±B[1],...,A[n1]±B[n1]}
AB={A[0]B[0],A[1]B[1],...,A[n1]B[n1]}
AB={ij=0A[i]B[j],...,ij=n1A[i]B[j]}
=(A0B0+A1B1,A0B1+A1B0)

易證 運算滿足交換律和結合律,且由乘法分配律易得:

A(B+C)=AB+AC

定義Fwt(A) 爲定義在數組A 上的一個運算,定義如下:
Fwt(A)={(Fwt(A0+A1),Fwt(A0A1))An>1n=1
性質1:
Fwt(A±B)=Fwt(A)±Fwt(B)
證明:容易發現Fwt(A) 的每一項都是A[0],A[1],...,A[n1] 的一個線性組合,故對加法滿足分配律

性質2:
Fwt(AB)=Fwt(A)Fwt(B)
證明:數學歸納法
n=1 時顯然成立
設該公式對於長度n/2 的數組均成立,則:
    Fwt(AB)
=Fwt(A0B0+A1B1,A0B1+A1B0)
=(Fwt(A0B0+A1B1+A0B1+A1B0) ,
      Fwt(A0B0+A1B1A0B1A1B0) )
=(Fwt((A0+A1)(B0+B1)),Fwt((A0A1)(B0B1)) )
=(Fwt(A0+A1)Fwt(B0+B1),Fwt(A0A1)Fwt(B0B1))
=(Fwt(A)0Fwt(B)0,Fwt(A)1Fwt(B)1)
=Fwt(A)Fwt(B)

公式真尼瑪長- - 是我證麻煩了麼- -

這樣我們就可以在O(n) 的時間內計算AB 了……等等
逆運算呢?
……

Dwt(Fwt(A))=Dwt(Fwt(A0+A1),Fwt(A0A1))
=Dwt(Fwt(A0)+Fwt(A1),Fwt(A0)Fwt(A1))
Dwt(A)=(Dwt(A0+A12),Dwt(A0A12))

Dwt(Fwt(A))=(Dwt(Fwt(A0)),Dwt(Fwt(A1)))
=(A0,A1)=A

完美。

變換時間複雜度O(nlogn) ,計算時間複雜度O(n) ,逆變換時間複雜度O(nlogn)

回來看題,令f[x][j] 表示第x個點爲根的連通誘導子圖中異或和爲j的數量,那麼f[x]={0,0,...,0,1,0,...}(f[y]+{1,0,0,...}) ,其中第一個數組的第a[x] 位爲1,其餘都爲0
用Fwt加速運算,時間複雜度O(mlogm+nm)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 1100
#define MOD 1000000007
using namespace std;

struct edge{
    int to,next;
}table[M<<1];
int head[M],tot;
void Add(int x,int y)
{
    table[++tot].to=y;
    table[tot].next=head[x];
    head[x]=tot;
}

int n,m,d;

void FWT(int a[],int n,int type/*1-FWT,-1-DWT*/)
{
    if(n==1) return ;
    for(int i=0;i<n>>1;i++)
    {
        a[i]+=a[i+(n>>1)];
        a[i+(n>>1)]=a[i]-(a[i+(n>>1)]<<1);
        a[i]%=MOD;(a[i+(n>>1)]+=MOD)%=MOD;
        if(type==-1)
        {
            a[i]=(MOD+1ll>>1)*a[i]%MOD;
            a[i+(n>>1)]=(MOD+1ll>>1)*a[i+(n>>1)]%MOD;
        }
    }
    FWT(a,n>>1,type);FWT(a+(n>>1),n>>1,type);
}

struct abcd{
    int a[M];
    abcd() {}
    abcd(bool)
    {
        memset(a,0,sizeof a);
    }
    int& operator [] (int x)
    {
        return a[x];
    }
    void FWT(int type)
    {
        ::FWT(a,d,type);
    }
    friend abcd operator + (abcd x,abcd y)
    {
        abcd z(true);
        for(int i=0;i<d;i++)
            z[i]=(x[i]+y[i])%MOD;
        return z;
    }
    friend abcd operator * (abcd x,abcd y)
    {
        abcd z(true);
        for(int i=0;i<d;i++)
            z[i]=((long long)x[i]*y[i])%MOD;
        return z;
    }
}f[M],zero,ans;

void Initialize()
{
    for(d=1;d<m;d<<=1);
    memset(f,0,sizeof f);
    memset(&zero,0,sizeof zero);
    memset(&ans,0,sizeof ans);
    zero[0]=1;zero.FWT(1);
    memset(head,0,sizeof head);
    tot=1;
}

void Tree_DP(int x,int from)
{
    for(int i=head[x];i;i=table[i].next)
        if(table[i].to!=from)
        {
            Tree_DP(table[i].to,x);
            f[x]=f[x]*(f[table[i].to]+zero);
        }
    ans=ans+f[x];
}

int main()
{
    int T;

    for(cin>>T;T;T--)
    {
        cin>>n>>m;
        Initialize();
        for(int i=1,x;i<=n;i++)
        {
            scanf("%d",&x);
            f[i][x]=1;
            f[i].FWT(1);
        }
        for(int i=1,x,y;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            Add(x,y);Add(y,x);
        }
        Tree_DP(1,0);
        ans.FWT(-1);
        for(int i=0;i<m;i++)
            printf("%d%c",ans[i],i==m-1?'\n':' ');
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章