bzoj 4033: [HAOI2015]树上染色

4033: [HAOI2015]树上染色

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 1886  Solved: 805

Description

有一棵点数为N的树,树边有边权。给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并

将其他的N-K个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益。

问收益最大值是多少。

Input

第一行两个整数N,K。

接下来N-1行每行三个正整数fr,to,dis,表示该树中存在一条长度为dis的边(fr,to)。

输入保证所有点之间是联通的。

N<=2000,0<=K<=N

Output

输出一个正整数,表示收益的最大值。

Sample Input

5 2

1 2 3

1 5 1

2 3 1

2 4 2

Sample Output

17

【样例解释】

将点1,2染黑就能获得最大收益。

题解:

这个题目伤透了我的心,注意一定要全开long  long....

好了,这个题目和树形背包十分类似,状态十分显然,我们设dp[i][j] 表示以i为节点的黑点个数是j的最大价值,怎么转移呢?这里十分巧妙,我们直接算是不可能的,那样就成了暴力,所以我们可以考虑每条边对整个答案的贡献,因为是两两点对,所以是子树中白点的数量乘以不在子树中的白点数量加上黑点的,所以状态转移就是dp[now][j+have]=max(dp[now][j+have],dp[now][have]+dp[to][j]+quan*j*(k-j)+(ll)(quan*(size[to]-j)*(n-k-size[to]+j)));后面的两个表示变对白点的贡献和边对黑点的贡献,然后我们跑一边01背包就可以了。最后不要忘记将子的结果和i节点合并。

代码

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<cstring>
#define ll long long
const int MAXN=3005;
using namespace std;
ll dp[MAXN][MAXN];
ll size[MAXN];
ll n,k,num=0;
struct edge{
    ll first;
    ll next;
    ll to;
    ll quan;
}a[MAXN*2];

void addedge(ll from,ll to,ll quan){
    a[++num].to=to;
    a[num].quan=quan;
    a[num].next=a[from].first;
    a[from].first=num;
}

void dfs(ll now,ll f){
    size[now]=1;
    for(ll i=a[now].first;i;i=a[i].next){
        ll to=a[i].to;
        if(to==f) continue;
        dfs(to,now);
        size[now]+=size[to];
    }
  ll fill=1;
    for(ll i=a[now].first;i;i=a[i].next){
        ll to=a[i].to,quan=a[i].quan;
        if(to==f) continue;
        for(ll have=fill;have>=0;have--){
            for(ll j=min(size[to],k);j>=0;j--){
                if(dp[to][j]>=0&&have+j<=k){
                    dp[now][j+have]=max(dp[now][j+have],dp[now][have]+dp[to][j]+quan*j*(k-j)+(ll)(quan*(size[to]-j)*(n-k-size[to]+j)));
                }
            }
        }
        fill=min(k,fill+size[to]);
    }
}

int main(){
    scanf("%lld%lld",&n,&k);
    memset(dp,0,sizeof(dp));
    memset(size,0,sizeof(size));
    for(int i=1;i<n;i++){
        ll x,y,z;scanf("%lld%lld%lld",&x,&y,&z);
        addedge(x,y,z);
        addedge(y,x,z);
    }
    dfs(1,0);
    printf("%lld\n",dp[1][k]);
}
时间: 2024-10-18 06:08:24

bzoj 4033: [HAOI2015]树上染色的相关文章

【BZOJ】4033: [HAOI2015]树上染色 树上背包

[题目]#2124. 「HAOI2015」树上染色 [题意]给定n个点的带边权树,要求将k个点染成黑色,使得 [ 黑点的两两距离和+白点的两两距离和 ] 最大.n<=2000. [算法]树上背包 [题解]设f[i][j]表示子树i中有j个黑点对答案的贡献(包括点 i 到父亲的边 p ),由于边p的贡献只和 j 有关,所以最后再统计. 所以做树上背包即可,注意这题特殊在f[x][0]≠0,所以初始f[x][k]+=f[y][0],然后不要把0作为物品. 最后统计边p的贡献:w[p] *(子树内黑点

BZOJ 4033: [HAOI2015]树上染色

题解: 树形DP 思路,考虑每条边的贡献,即这条边两边的黑点数量相乘+白点数量相乘再成边长 #include<iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn=2009; typedef long long Lint; int n,m; int cntedge=0; int head[maxn]={0}; int to[maxn<<1],nex[m

bzoj4033: [HAOI2015]树上染色(树形dp)

4033: [HAOI2015]树上染色 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 3269  Solved: 1413[Submit][Status][Discuss] Description 有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并 将其他的N-K个点染成白色.将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益. 问收益最大值是多少. Input 第一

【BZOJ4033】[HAOI2015]树上染色 树形DP

[BZOJ4033][HAOI2015]树上染色 Description 有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染成白色.将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益.问收益最大值是多少. Input 第一行两个整数N,K. 接下来N-1行每行三个正整数fr,to,dis,表示该树中存在一条长度为dis的边(fr,to). 输入保证所有点之间是联通的. N<=2000,0<=K&l

[HAOI2015]树上染色(树形dp)

[HAOI2015]树上染色 题目描述 有一棵点数为 N 的树,树边有边权.给你一个在 0~ N 之内的正整数 K ,你要在这棵树中选择 K个点,将其染成黑色,并将其他 的N-K个点染成白色 . 将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间的距离的和的受益.问受益最大值是多少. 输入输出格式 输入格式: 第一行包含两个整数 N, K .接下来 N-1 行每行三个正整数 fr, to, dis , 表示该树中存在一条长度为 dis 的边 (fr, to) .输入保证所有点之间是联通的

bzoj 4033: [HAOI2015]T1(树形DP)

4033: [HAOI2015]T1 Time Limit: 10 Sec  Memory Limit: 256 MB Submit: 819  Solved: 375 [Submit][Status][Discuss] Description 有一棵点数为 N 的树,树边有边权.给你一个在 0~ N 之内的正整 数 K ,你要在这棵树中选择 K个点,将其染成黑色,并将其他 的 N-K个点染成白色 . 将所有点染色后,你会获得黑点两两之间的距 离加上白点两两之间的距离的和的受益.问受益最大值是多

[BZOJ 4033] [HAOI2015] T1 【树形DP】

题目链接:BZOJ - 4033 题目分析 使用树形DP,用 f[i][j] 表示在以 i 为根的子树,有 j 个黑点的最大权值. 这个权值指的是,这个子树内部的点对间距离的贡献,以及 i 和 Father[i] 之间的边对答案的贡献(比如这条边对黑点对距离和的贡献就是子树内部的黑点数 * 子树外部的黑点数 * 这条边的权值). 然后DFS来求,枚举 i 的每个儿子 j,现在的 f[i][] 是包含了 [1, j-1] 子树,然后两重循环枚举范围是 [1, j - 1] 的子树总 Size 和

bzoj 4034: [HAOI2015]树上操作 树链剖分+线段树

4034: [HAOI2015]树上操作 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 4352  Solved: 1387[Submit][Status][Discuss] Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有

BZOJ 4034 HAOI2015 树上操作

4034: [HAOI2015]树上操作 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 6033  Solved: 1959[Submit][Status][Discuss] Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有