BZOJ 4033 HAOI2008 树上染色

4033: [HAOI2015]树上染色

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 2302  Solved: 973
[Submit][Status][Discuss]

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染黑就能获得最大收益。

HINT

2017.9.12新加数据一组 By GXZlegend 

Source

这道题需要知道一个小结论就是树上合并背包的复杂度是O(n^2)的
接下来就很显然了,我们用f[st][k]来表示以st为根的子树(包括st)选k个黑色节点对答案最大的贡献
我们来考虑一条边的贡献怎么算呢
 
很显然A-B这条边对答案的贡献为k*(m-k)*dis[AB]+(siz[A]-k)*(n-siz[A]-m+k)*dis[AB]
dp做背包即可 
#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read(){
    ll x=0;ll f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const ll MAXN=1e6+10;
namespace zhangenming{
    struct node{
        ll y,next,v;
    }e[MAXN];
    ll linkk[MAXN],len=0,n,m,siz[MAXN]={},mm,f[2100][2100]={},tmp[2010]={};
    inline void insert(ll xx,ll yy,ll vv){
        e[++len].y=yy;e[len].next=linkk[xx];e[len].v=vv;linkk[xx]=len;
    }
    void init(){
        n=read();m=read();mm=n-m;
        for(ll i=1;i<n;i++){
            ll xx=read();ll yy=read();ll vv=read();
            insert(xx,yy,vv);insert(yy,xx,vv);
        }
    }
    inline void dp(ll st,ll father){
        siz[st]=1;
        for(ll i=linkk[st];i;i=e[i].next){
            if(e[i].y!=father){
                dp(e[i].y,st);
                memset(tmp,-1,sizeof(tmp));
                for(ll j=0;j<=siz[st];j++){
                    for(ll k=0;k<=siz[e[i].y];k++){
                        tmp[j+k]=max(tmp[j+k],f[st][j]+f[e[i].y][k]+(m-k)*k*e[i].v+(siz[e[i].y]-k)*(mm-siz[e[i].y]+k)*e[i].v);
                    }
                }
                siz[st]+=siz[e[i].y];
                for(int j=0;j<=siz[st];j++){
                    f[st][j]=tmp[j];
                }
            }
        }
    }
}
int main(){
    using namespace zhangenming;
    init();
    dp(1,0);
    cout<<f[1][m]<<endl;
    return 0;
}

  

 

posted @ 2017-11-23 15:38  zhangenming  阅读(131)  评论(0编辑  收藏  举报