• BZOJ4033:[HAOI2015]树上染色——题解


    https://www.lydsy.com/JudgeOnline/problem.php?id=4033

    有一棵点数为N的树,树边有边权。给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益。
    问收益最大值是多少。

    emmm……人傻自然 $~O(nk)->O(nk^2)~$

    参考:https://www.luogu.org/blog/mlystdcall/solution-p3177

    设$f[i][j]$表示以$i$为根子树染$j$个点的最大收益……emm脑内想想就知道需要维护一大堆东西,一点也不优美(orz隔壁这么做出来的胡神犇)

    参阅题解之后发现我们其实大可以按边算贡献来求总价值!

    于是有了这个想法我们很快能列出来一条边,边权为w的贡献应为(边一头的黑点数)*(边另一头的黑点数)*w+(边一头的白点数)*(边一头的白点数)*w

    (讲真很难想……没有一丝提示……也可能是我做题做少了……)

    那么我们的$f[i][j]$的含义就变成了$i$子树里的边所能提供的最大贡献是多少,也就变成了树上背包问题了。

    这个问题有经典的$O(nk)$算法,只要不像我写的那么丑就没有问题233。

    #include<map>
    #include<cmath>
    #include<stack>
    #include<queue>
    #include<cstdio>
    #include<cctype>
    #include<vector>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int N=2005;
    const ll INF=1e18;
    inline int read(){
        int X=0,w=0;char ch=0;
        while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
        while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
        return w?-X:X;
    }
    struct node{
        int to,nxt,w;
    }e[N*2];
    int n,k,cnt,head[N],sz[N];
    ll f[N][N];
    inline void add(int u,int v,int w){
        e[++cnt].to=v;e[cnt].w=w;e[cnt].nxt=head[u];head[u]=cnt;
    }
    void dfs(int u,int fa){
        sz[u]=1;f[u][0]=f[u][1]=0;
        for(int i=2;i<=k;i++)f[u][i]=-INF;
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].to,w=e[i].w;
            if(v==fa)continue;
            dfs(v,u);
            for(int i=min(k,sz[u]);i>=0;i--){
                for(int j=min(k-i,sz[v]);j>=0;j--){
                    ll val=(ll)j*(k-j)*w+(ll)(sz[v]-j)*(n-sz[v]-k+j)*w;
                    f[u][i+j]=max(f[u][i+j],f[u][i]+f[v][j]+val);
                }
            }
            sz[u]+=sz[v];
        }
    }
    int main(){
        n=read(),k=read();
        for(int i=1;i<n;i++){
            int u=read(),v=read(),w=read();
            add(u,v,w);add(v,u,w);
        }
        dfs(1,0);
        printf("%lld
    ",f[1][k]);
        return 0;
    }

    +++++++++++++++++++++++++++++++++++++++++++

     +本文作者:luyouqi233。               +

     +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

    +++++++++++++++++++++++++++++++++++++++++++

  • 相关阅读:
    人民币格式化 ,分割
    解决IE下页面空白或者报错:[vuex] vuex requires a Promise polyfill in this browser
    js生成图片
    适用于iview的表格转Excel插件
    js金额转大写数字
    webstorm vue cli 热更新不起作用解决办法
    纯css实现 switch开关
    vue 时间戳转 YYYY-MM-DD h:m:s
    Simple2D-20(重构)
    Simple2D-19(音乐播放器)播放器的源码实现
  • 原文地址:https://www.cnblogs.com/luyouqi233/p/9213289.html
Copyright © 2020-2023  润新知