zoukankan      html  css  js  c++  java
  • 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("
    ");
        }
        return 0;
    }

     

  • 相关阅读:
    关于SSIS中解密数据库字符串的方法
    Excel-计算年龄、工龄 datedif()
    Excel-满足指定条件并且包含数字的单元格数目,DCOUNT()
    Oracle-累加功能,累加百分比
    Oracle-一张表中增加计算某列值重复的次数列,并且把表中其他列也显示出来,或者在显示过程中做一些过滤
    mysql 实现某年单季度内的品牌TOPn销量在此年此单季度内销量占比
    mysql 分组统计、排序、取前N条记录解决方案
    mysql 除法运算保留小数的用法
    mysql 不等于 符号写法
    mysql 中@ 和 @@的区别
  • 原文地址:https://www.cnblogs.com/FrankChen831X/p/11408115.html
Copyright © 2011-2022 走看看