• [BZOJ 4033] 树上染色


    Link:

    BZOJ 4033 传送门

    Solution:

    此题用到了计算贡献的方法,

    将 多条路径的路径和  $->$ $sum_{i=1}^{n-1} w[i]*cnt[i]$

    这样我们由找出所有路径再计算转化成了对每条边计算其的贡献

    由于所有节点只用2种选择,接下来就是比较套路的树形DP了

    设 $dp[i][j]$ 为在以 $i$ 为根的子树中,有$j$个黑点时的$MAX$。

    这样按照$dfs$序依次处理每个节点$x$,对子树背包$DP$,最后再加上$w_{<x,father[x]>}$的贡献即可

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> P;
    
    const int MAXN=2005*2;
    vector<P> G[MAXN];
    ll n,k,dp[MAXN][MAXN],sz[MAXN];
    
    void tree_dp(int x,int anc,ll val)
    {
        sz[x]=1;
        for(int i=0;i<G[x].size();i++)
        {
            int v=G[x][i].first;
            if(v==anc) continue;
            tree_dp(v,x,G[x][i].second);
            for(int p=min(sz[x],k);p>=0;p--)
                for(int q=min(sz[v],k-p);q>=0;q--)
                    dp[x][p+q]=max(dp[x][p+q],dp[x][p]+dp[v][q]);
            sz[x]+=sz[v];
        }
        for(int i=0;i<=min(sz[x],k);i++) //统计贡献
            dp[x][i]+=val*((ll)i*(k-i)+(sz[x]-i)*((n-sz[x])-(k-i)));
    }
    
    int main()
    {
        scanf("%lld%lld",&n,&k);
        for(int i=1;i<n;i++)
        {
            ll x,y,z;scanf("%lld%lld%lld",&x,&y,&z);
            G[x].push_back(P(y,z));
            G[y].push_back(P(x,z));
        }
        tree_dp(1,0,0);
        printf("%lld",dp[1][k]);
        return 0;
    }

    Review:

     1、计算贡献的思想:

    多个整体 拆分成 每个个体$*$出现次数的和

    只要能快速地计算出贡献,依次考虑每个个体即可

    2、树形$DP$的套路和注意事项:

    套路:树形$DP$大部分时候都是依据$dfs$序在对子树背包$DP$

    Note:(1)背包$DP$要从后往前更新,防止多次计算

               (2)一般$size[x]$的更新都要放在对该子树更新完之后

    3、点集的划分

    该题的特殊性在于对点集严格划分为2堆,才能直接算出每种点的个数

    当出现对点集严格划分的题目时,只要保存一个量,并考虑能否计算贡献

  • 相关阅读:
    U盘安装Ubuntu 10.4 Server
    MySQL操作使用
    Fedora17安装MySQL及配置
    笔记:重构
    Java 并发之线程安全
    Java 并发之共享对象
    UTF8 与 UTF16 编码
    matplotlib 初使用
    用 ggplot2 在同一个图上画多条颜色不同的线
    完成情况(一)
  • 原文地址:https://www.cnblogs.com/newera/p/9138600.html
Copyright © 2020-2023  润新知