1. 程式人生 > >[HAOI2015]樹上染色(樹形dp)

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

long long 價值 記錄 接下來 tin ++ 我們 不用 include

[HAOI2015]樹上染色

題目描述

有一棵點數為 N 的樹,樹邊有邊權。給你一個在 0~ N 之內的正整數 K ,你要在這棵樹中選擇 K個點,將其染成黑色,並將其他 的N-K個點染成白色 。 將所有點染色後,你會獲得黑點兩兩之間的距離加上白點兩兩之間的距離的和的受益。問受益最大值是多少。

輸入輸出格式

輸入格式:

第一行包含兩個整數 N, K 。接下來 N-1 行每行三個正整數 fr, to, dis , 表示該樹中存在一條長度為 dis 的邊 (fr, to) 。輸入保證所有點之間是聯通的。

輸出格式:

輸出一個正整數,表示收益的最大值。

輸入輸出樣例

輸入樣例#1: 復制

3 1
1 2 1

1 3 2

輸出樣例#1: 復制

3

說明

對於 100% 的數據, 0<=K<=N <=2000

題解

最開始我以為要處理出點與點之間的距離。
然後對於k的話實際上就是min(k,n-k)。
然後dp出最小價值的k個點對。
拿總路徑和減去最小dp值。
但是發現不好維護。

於是看了題解
對於一個子樹內我要選取的黑點。
我們這一次dp的不僅是增加的黑點的價值,還要處理出減少的白點的價值。
也就是說每選一個點,就要判斷這條路徑的貢獻變化了多少。
對於每條路徑的貢獻。
為當前子樹的黑節點×子樹外的黑節點×邊權+當前子樹的白節點×子樹外的白節點×邊權就可以了。
這樣就不用刻意去記錄點對了。

代碼

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int N=2001;
struct node{
    int to,nex;
    ll v;
}e[N<<2];
int n,k,num,head[N],size[N];
ll f[N][N];
ll read(){
    ll x=0,w=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*w;
}

void add(int from,int to,int v){
    num++;
    e[num].to=to;
    e[num].v=v;
    e[num].nex=head[from];
    head[from]=num;
}

void dfs1(int x,int fa){
    size[x]=1;
    for(int i=head[x];i;i=e[i].nex){
        int v=e[i].to;
        if(v==fa)continue;
        dfs1(v,x);size[x]+=size[v];
    }
}

void dfs2(int x,int fa){
    f[x][0]=0;f[x][1]=0;
    for(int i=head[x];i;i=e[i].nex){
        int v=e[i].to;if(v!=fa){dfs2(v,x);
        for(int j=min(size[x],k);j>=0;j--){
            for(int l=0;l<=min(size[v],j);l++)
            if(f[x][j-l]!=-1){
            ll val=1ll*(l)*(k-l)*e[i].v+1ll*(size[v]-l)*(n-k+l-size[v])*e[i].v;
            f[x][j]=max(f[x][j-l]+f[v][l]+val,f[x][j]);
            }
            }
        }
    }
}

int main(){
    n=read();k=read();
    for(int i=1;i<n;i++){
        int  x=read(),y=read(),z=read();
        add(x,y,z);add(y,x,z);
    }
    memset(f,-1,sizeof(f));
    dfs1(1,1);dfs2(1,1);
    printf("%lld\n",f[1][k]);
    return 0;
}

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