[hdu5909]Tree Cutting——动态规划+FWT

题目大意:

给定一棵树,求有多少个联通块满足异或和=k,对于每一个k求答案。

思路:

\(dp_{i,j}\)表示联通块深度最小的点为i时,异或和为j时有多少个满足条件。
从儿子转移,FWT优化即可。

/*=======================================
 * Author : ylsoi
 * Time : 2019.2.10
 * Problem : hdu5909
 * E-mail : [email protected]
 * ====================================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
    freopen("hdu5909.in","r",stdin);
    freopen("hdu5909.out","w",stdout);
}

template<typename T>void read(T &_){
    _=0; T f=1; char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
    _*=f;
}

const int maxn=1000+10;
const int maxm=2048+10;
const int mod=1e9+7;
const int inv=(mod+1)/2;
int T,n,m,w[maxn];
vector<int>G[maxn];
ll dp[maxn][maxm],ans[maxm];

void fwt(ll *A,int ty){
    for(int len=1;len<m;len<<=1)
        for(int L=0;L<m;L+=len<<1)
            REP(i,L,L+len-1){
                ll x=A[i],y=A[i+len];
                A[i]=(x+y)%mod;
                A[i+len]=(x-y)%mod;
                if(ty==-1)A[i]=A[i]*inv%mod,A[i+len]=A[i+len]*inv%mod;
            }
}

void dfs(int u,int fh){
    REP(i,0,m)dp[u][i]=0;
    dp[u][w[u]]=1;
    fwt(dp[u],1);
    REP(i,0,G[u].size()-1){
        int v=G[u][i];
        if(v==fh)continue;
        dfs(v,u);
        REP(j,0,m-1)dp[u][j]=dp[u][j]*dp[v][j]%mod;
    }
    fwt(dp[u],-1),++dp[u][0],fwt(dp[u],1);
}

int main(){
    File();
    read(T);
    while(T--){
        read(n),read(m);
        REP(i,1,n)read(w[i]);
        int u,v;
        REP(i,1,n-1){
            read(u),read(v);
            G[u].pb(v);
            G[v].pb(u);
        }
        dfs(1,0);
        REP(i,1,n)fwt(dp[i],-1);
        REP(i,0,m-1){
            ans[i]=0;
            REP(j,1,n)ans[i]=(ans[i]+dp[j][i])%mod;
            if(!i)ans[i]=((ans[i]-n)%mod+mod)%mod;
            printf("%lld%c",ans[i],i==m-1 ? '\n' : ' ');
        }
        REP(i,1,n)G[i].clear();
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ylsoi/p/10359368.html