hdoj5909 Tree Cutting(点分治+树上dp转序列dp)

题目链接:https://vjudge.net/problem/HDU-5909

题意:给一颗树,结点带权值v[i]<m。求异或和为k的子树个数(0<=k<m)。

思路:

  首先点分治处理一颗树,跑一遍dfs得到该树的dfs序。然后我们用序列dp来做,用dp[i][j]表示必须包括重心,处理序列中第i个结点时异或和为j的子树个数,因为必须包括重心,所以能做到不重不漏。

  现在讨论结点i已经决策完毕

    如果选i+1:dp[i+1][j^v[id[i+1]]]+=dp[i][j]。(id[i+1]表示dfs序列中第i+1个结点的编号)。

    如果不选i+1:dp[i+sz[id[i+1]]][j]+=dp[i][j]。(因为如果i+1不选的话,以i+1为根的子树里的结点都不能选,sz[i]表示结点i的子树的大小)。

  最后ans[i]+=dp[t][i],t为该子树的大小。

AC代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

const int maxn=1e3+50;
const int inf=0x3f3f3f3f;
const int MOD=1e9+7;
typedef long long LL;
struct node{
    int v,nex;
}edge[maxn<<1];
int T,n,m,V[maxn],head[maxn],cnt;
int sz[maxn],mson[maxn],Min,root,size,vis[maxn],id[maxn],t;
LL ans[maxn],dp[maxn][maxn];

void adde(int u,int v){
    edge[++cnt].v=v;
    edge[cnt].nex=head[u];
    head[u]=cnt;
}

void getroot(int u,int fa){
    sz[u]=1,mson[u]=0;
    for(int i=head[u];i;i=edge[i].nex){
        int v=edge[i].v;
        if(vis[v]||v==fa) continue;
        getroot(v,u);
        sz[u]+=sz[v];
        mson[u]=max(mson[u],sz[v]);
    }
    mson[u]=max(mson[u],size-sz[u]);
    if(mson[u]<Min) Min=mson[u],root=u;
}

void dfs(int u,int fa){
    sz[u]=1,id[++t]=u;
    for(int i=head[u];i;i=edge[i].nex){
        int v=edge[i].v;
        if(v==fa||vis[v]) continue;
        dfs(v,u);
        sz[u]+=sz[v];
    }
}

void solve(int u){
    t=0;
    dfs(u,0);
    for(int i=1;i<=t;++i)
        for(int j=0;j<m;++j)
            dp[i][j]=0;
    dp[1][V[u]]=1;
    for(int i=1;i<=t-1;++i)
        for(int j=0;j<m;++j){
            dp[i+1][j^V[id[i+1]]]=(dp[i+1][j^V[id[i+1]]]+dp[i][j])%MOD;
            dp[i+sz[id[i+1]]][j]=(dp[i+sz[id[i+1]]][j]+dp[i][j])%MOD;
        }
    for(int i=0;i<m;++i)
        ans[i]=(ans[i]+dp[t][i])%MOD;
}

void fenzhi(int u,int ssize){
    vis[u]=1;
    solve(u);
    for(int i=head[u];i;i=edge[i].nex){
        int v=edge[i].v;
        if(vis[v]) continue;
        Min=inf,root=0,size=sz[v];
        getroot(v,0);
        fenzhi(root,size);
    }
}

int main(){
    scanf("%d",&T);
    while(T--){
        cnt=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i)
            head[i]=vis[i]=0;
        for(int i=1;i<=n;++i)
            scanf("%d",&V[i]);
        for(int i=0;i<m;++i)
            ans[i]=0;
        for(int i=1;i<n;++i){
            int u,v;
            scanf("%d%d",&u,&v);
            adde(u,v);
            adde(v,u);
        }
        Min=inf,root=0,size=n;
        getroot(1,0);
        fenzhi(root,n);
        for(int i=0;i<m;++i){
            printf("%lld",ans[i]);
            if(i!=m-1) printf(" ");
        }
        printf("\n");
    }
    return 0;
}

 

 

posted @ 2019-08-25 15:42  Frank__Chen  阅读(212)  评论(0编辑  收藏  举报