Hdu 5909 Tree Cutting

n(n1000) 個點的樹上每個點有一個權值(0Ci<m,m210) ,定義一個子樹的權值爲這個子樹節點的權值的異或。分別求權值爲[0,m1] 的子樹的個數mod(109+7)


對於一個無根樹的子樹,如果我們欽定一個點作爲無根樹的根,那麼對於這個樹的所有子樹都可以欽定出唯一的一個點作爲根。

考慮樹dp ,記dpst,v 爲以st爲根,權值爲v 的子樹個數

那麼對於一個狀態dpst 我們有加入st 的一個子樹x 之後的新的狀態dpnst 的值dpnst,v=dpstv+ijdpst,i×dpx,j


直接這麼樹dp 的複雜度是O(n×m2) 的,我們意外的發現中間的這個ijdpst,i×dpx,j 就是一個xor卷積,於是可以用FWT將他從O(m2) 優化到 O(m×log(m)) ,總的複雜度優化到O(n×m×log(m)) 就可以通過此題


#include<bits/stdc++.h>
using namespace std;

#define LL int 
const int maxn = 1123,mod = 1e9+7;
const int v2 = (mod + 1) / 2;

vector<int> edge[maxn];
int val[maxn],m;

LL dp[maxn][maxn];
LL tem[maxn];

void fwt (LL a[] , int n ) {
    for ( int d = 1 ; d < n ; d <<= 1 ) {
        for ( int k = d << 1 , i = 0 ; i < n ; i += k ) {
            for ( int j = 0 ; j < d ; ++ j ) {
                LL x = a[i + j] , y = a[i + j + d] ;
                a[i + j] = ( x + y ) % mod ;
                a[i + j + d] = ( x - y + mod ) % mod ;
            }
        }
    }
}

void ufwt (LL a[] , int n ) {
    for ( int d = 1 ; d < n ; d <<= 1 ) {
        for ( int k = d << 1 , i = 0 ; i < n ; i += k ) {
            for ( int j = 0 ; j < d ; ++ j ) {
                LL x = a[i + j] , y = a[i + j + d] ;
                a[i + j] = 1LL * ( x + y ) * v2 % mod ;
                a[i + j + d] = 1LL * ( x - y + mod ) * v2 % mod ;
            }
        }
    }
}

void solve(LL *a,LL *b){
    for(int i=0;i<m;i++) tem[i] = a[i]; 
    fwt(a,m),fwt(b,m);
    for(int i=0;i<m;i++)
        a[i] = (1ll * a[i] * b[i]) % mod;
    ufwt(a,m),ufwt(b,m);
    for(int i=0;i<m;i++) 
        a[i]=(a[i]+tem[i]) %mod;
}

void dfs(int st,int fa){
    dp[st][val[st]] = 1;
    for(auto x : edge[st]){
        if(x == fa) continue;
        dfs(x,st);
        solve(dp[st],dp[x]);
    }
}

LL ans[maxn];

int main(){
    int T,n;
    scanf("%d",&T);
    while(T-- && ~scanf("%d %d",&n,&m)){
        for(int i=0;i<=n;i++) edge[i].clear();
        int x,y;
        for(int i = 1;i <= n;i++) scanf("%d",&val[i]);
        int l,r;
        for(int i=1;i<n;i++){
            scanf("%d %d",&l,&r);
            edge[l].push_back(r);
            edge[r].push_back(l);
        }
        memset(dp,0,sizeof(dp));
        dfs(1,-1);
        memset(ans,0,sizeof(ans));
        for(int i=0;i<m;i++){
            for(int j=1;j<=n;j++)
                (ans[i] += dp[j][i]) %= mod;
        }
        for(int i=0;i<m;i++)
            printf(i<m-1?"%d ":"%d\n",ans[i]);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章