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]);
}

 

posted @ 2017-08-09 21:47  人间失格—太宰治  阅读(262)  评论(1编辑  收藏  举报