【樹形DP+FWT】HDU5909[Tree Cutting]題解

題目概述

定義一棵樹 {vn} 的權值爲 v1 xor v2 xor v3 xorvn 。給出一棵樹,統計權值 [0,m) 在這棵樹子圖(子圖顯然也是樹)中的出現情況。

解題報告

定義 f[i][j] 表示以 i 爲根異或權值爲 j 的方案數,對於 i 的兒子 son ,我們可以進行轉移:

f[i][j]=kt=jf[i][k]f[son][t]

顯然是位運算卷積,上FWT就行了。

示例程序

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
const int maxn=1000,maxm=1024,MOD=1e9+7,INV2=MOD+1>>1;

int te,n,m,v[maxn+5],f[maxn+5][maxm],ans[maxm];
int E,lnk[maxn+5],nxt[(maxn<<1)+5],son[(maxn<<1)+5];

#define Add(x,y) son[++E]=(y),nxt[E]=lnk[x],lnk[x]=E
inline void AMOD(int &x,int tem) {if ((x+=tem)>=MOD) x-=MOD;}
inline void FWT(int *a,int n,int f){
    for (int k=1;k<n;k<<=1)
        for (int i=0;i<n;i+=k<<1)
            for (int j=0;j<k;j++){
                int x=a[i+j],y=a[i+j+k];AMOD(a[i+j]=x,y);AMOD(a[i+j+k]=x,MOD-y);
                if (f<0) a[i+j]=(LL)a[i+j]*INV2%MOD,a[i+j+k]=(LL)a[i+j+k]*INV2%MOD;
            }
}
void DP(int x,int pre=0){
    int *fx=f[x];memset(f[x],0,sizeof(f[x]));fx[v[x]]=1;
    //這裏不能寫成memset(fx,0,sizeof(fx)),會出現所謂的指針降級錯誤
    for (int j=lnk[x];j;j=nxt[j])
        if (son[j]!=pre){
            int tem[m];for (int i=0;i<m;i++) tem[i]=fx[i];
            DP(son[j],x);int *fs=f[son[j]];FWT(tem,m,1);FWT(fs,m,1);
            for (int i=0;i<m;i++) tem[i]=(LL)tem[i]*fs[i]%MOD;
            FWT(tem,m,-1);for (int i=0;i<m;i++) AMOD(fx[i],tem[i]);
        }
        for (int i=0;i<m;i++) AMOD(ans[i],fx[i]);
}
int main(){
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    for (scanf("%d",&te);te;te--){
        E=0;memset(lnk,0,sizeof(lnk));memset(ans,0,sizeof(ans));
        scanf("%d%d",&n,&m);for (int i=1;i<=n;i++) scanf("%d",&v[i]);
        for (int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),Add(x,y),Add(y,x);
        DP(1);for (int i=0;i<m-1;i++) printf("%d ",ans[i]);printf("%d\n",ans[m-1]);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章