• [HAOI2015]树上染色


    按点来算贡献的话并不好算。考虑到路径长度和可以拆分成每条边的长度乘上其被经过的次数,所以统计边的贡献就可以了。树上一条边会将树分成两边,而该边被经过的次数实质上就是两边的白点个数之积加上黑点个数积。因为定了根,所以其中一边可以按子树考虑,于是就转换成了树形dp。

    #include<stdio.h>
    #define ll long long
    #define N 2007
    
    struct E{
        int next,to;
        ll dis;
    }e[N<<1];
    int head[N],cnt=0,n,m;
    ll dp[N][N],sz[N];
    
    inline void add(int id,int to,ll dis){
        e[++cnt]=(E){head[id],to,dis};
        head[id]=cnt;
    }
    
    inline int min(int x,int y){return x<y? x:y;}
    inline ll max(ll x,ll y){return x>y? x:y;}
    void dfs(int u,int fa){
        sz[u]=1;
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].to;
            if(v==fa) continue;
            dfs(v,u);
            for(int j=sz[u];~j;j--)
                for(int k=sz[v];~k;k--)
                    dp[u][j+k]=max(dp[u][j+k],dp[u][j]+dp[v][k]+e[i].dis*(1LL*k*(m-k)+1LL*(sz[v]-k)*(n-m-sz[v]+k)));
            sz[u]+=sz[v];
        }
    }
    
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<n;i++){
            int u,v; ll dis;
            scanf("%d%d%lld",&u,&v,&dis);
            add(u,v,dis),add(v,u,dis);
        }
        dfs(1,0);
        printf("%lld",dp[1][m]);
    }
    

    Tips

    之前dp转移的时候是正序枚举的,然后WA了。看了很多题解之后,也并没有弄清,实际上很多人自己都没有弄清楚就在乱写题解。这道题的 (j)(k) 都要倒序枚举,回忆做 01 背包的时候也要倒序枚举,是因为有一维状态被滚掉了。会看这道题,也有一维被滚掉了,本来的状态应该是 (dp[u][i][j+k]) ,表示在子树 (u) 中的前 (i) 棵子树中选 (j+k) 个白点的最大贡献,而更新时用的是 (dp[u][i-1][j+k])(dp[u][i-1][j]) 。删掉 (i) 这一维,就要保证 (j+k) 要在 (j) 之前更新,那么 (j)(k) 都要倒序就很显然了。

  • 相关阅读:
    .net core 经典面试题
    面试常问概念类问题
    常见 .net 面试题目
    Linux 最常用150个命令汇总
    .net core 国际化(web通用版)
    vim 命令合集
    解决Mariadb安装时的Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-install-qenllaxj/mysqlclient/报错
    正则表达式
    python中的JWT
    chapter2.3、react高阶组件,装饰器
  • 原文地址:https://www.cnblogs.com/wwlwQWQ/p/14123859.html
Copyright © 2020-2023  润新知