zoukankan      html  css  js  c++  java
  • Codeforces 1240C. Paint the Tree

    传送门

    首先每个点 $u$ 只能选择不超过 $k$ 个相连的边

    并且设边为 $(u,v)$ ,那么此时 $v$ 也必须选择这条边

    因为图是一颗树,显然考虑一下树形 $dp$

    设 $f[x][0/1]$ 表示考虑完 $x$ 的子树,当前节点有没有留一个选择给和父亲相连的边($0$ 表示没有)

    那么对于 $f[x][0]$

    考虑所有 $x$ 的儿子 $v$,我们要选出不超过 $k$ 个儿子的 $f[v][1]+val(x,v)$ ,然后剩下的儿子全部选 $f[v][0]$ ,求最大价值

    (其中 $val(x,v)$ 是边 $(x,v)$ 的价值)

    考虑一开始所有的儿子都先选 $f[v][0]$,对于某个儿子 $v$ 如果我们之后要选 $f[v][1]$ ,那么增加的贡献 $delta$ 为 $-f[v][0]+f[v][1]+val(x,v)$

    显然 $-f[v][0]$ 是因为之前已经加入了 $f[v][0]$ 的贡献

    那么此时每个儿子的选择互不影响,直接按 $delta$ 排序取前 $k$ 大即可(注意如果还没到 $k$ 个但是 $delta$ 已经小于 $0$ 了就不用选)

    对于 $f[x][1]$ 也是一样的道理,但是我们这时候取的就是前 $k-1$ 的的 $delta$ 了

    不妨设 $1$ 为根,那么答案即为 $f[1][0]$

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<vector>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=5e5+7;
    int Q,n,K;
    int fir[N],from[N<<1],to[N<<1],val[N<<1],cntt;
    inline void add(int a,int b,int c) { from[++cntt]=fir[a]; fir[a]=cntt; to[cntt]=b; val[cntt]=c; }
    ll f[N][2];
    void dfs(int x,int fa)
    {
        vector <ll> tmp;
        for(int i=fir[x];i;i=from[i])
        {
            int v=to[i]; if(v==fa) continue;
            dfs(v,x);
            f[x][0]+=f[v][0]; f[x][1]+=f[v][0];
            tmp.push_back(f[v][1]-f[v][0]+val[i]);
        }
        sort(tmp.begin(),tmp.end()); reverse(tmp.begin(),tmp.end());
        int sz=tmp.size();
        for(int i=0;i<K&&i<sz;i++)
        {
            if(tmp[i]<=0) break;
            f[x][0]+=tmp[i];
            if(i<K-1) f[x][1]+=tmp[i];
        }
    }
    int main()
    {
        Q=read();
        while(Q--)
        {
            n=read(),K=read(); int a,b,c;
            for(int i=1;i<=n;i++) f[i][0]=f[i][1]=0;
            for(int i=1;i<=n;i++) fir[i]=0; cntt=0;
            for(int i=1;i<n;i++)
            {
                a=read(),b=read(),c=read();
                add(a,b,c); add(b,a,c);
            }
            dfs(1,0);
            printf("%lld
    ",f[1][0]);
        }
        return 0;
    }
  • 相关阅读:
    IOS 学习 开发 自定义 UINavigationController 导航
    -canOpenURL: failed for URL: ""
    一种简单的直观的高效的权限设计
    查询最近24小时、昨天、最近7天、上周、最近30天、上月的数据记录
    MS SQLSERVER中如何快速获取表的记录总数
    MVC MVVM和传统三层的理解
    C# 后台模拟前台post发送json数据
    C# Post发送数据返回页面结果
    CSS同时使用背景图片和背景颜色
    C# 根据日期计算星期几
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/11632480.html
Copyright © 2011-2022 走看看