[HAOI2015]树上染色

两点之间贡献和的问题转化成边的贡献

已经深搜过的点的个数为son[u] 回溯得到的另外一个子树的个数为son[v]

然后每一条边的贡献分别由黑点和白点组成

设遍历到的树边靠近子树的一端黑点为x, 即 黑内为x 黑外 为 K - x 那么黑点的贡献为x * (son[v] - x)

白内为 son[v] - x 白外为(n - son[v]  - (  son[v] - x ) ) 白点的贡献为 前面两者相乘

 

#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>

typedef long long ll;
using namespace std;

const ll inf = 0x3f3f3f3f;

using namespace std;
const ll maxn = 2e3+10;
const ll maxm = 2e3+10;
struct node {
    ll v, w;
    ll nxt;
}edge[maxm * 2];
ll head[maxn], tot;
void Insert(ll u, ll v, ll w) {
    edge[++tot].v = v;
    edge[tot].w = w;
    edge[tot].nxt = head[u];
    head[u] = tot;
}
ll son[maxn];
ll n, m, K;
ll dp[maxn][maxn];
ll f(ll x, int num, int v) {
    return x * num * (K - num) + x * (n - son[v] - (K - num)) * (son[v] - num);
}
void dfs(ll u) {
    son[u] = 1;
    for (ll i = head[u]; i; i = edge[i].nxt) {
        int v = edge[i].v;
        if (son[v]) {continue;}
        dfs(v);
        for (ll j = min(son[u], K); j >= 0; j--) {
            for (int k = min(K, son[v]); k >= 0; k--)
                dp[u][j + k] = max(dp[u][j + k], dp[u][j] + dp[v][k] + f(edge[i].w, k, v));
        }
        son[u] += son[v];
    }
}


int main () {
    scanf("%lld%lld", &n, &K);
    for (ll i = 1; i < n; i++) {
        ll u, v, w;
        scanf("%lld%lld%lld", &u, &v, &w);
        Insert(u, v, w);
        Insert(v, u, w);
    }
    dfs(1);
    printf("%lld\n", dp[1][K]);
    return 0;
} 

 

posted @ 2019-10-19 21:29  czwccc  阅读(119)  评论(0编辑  收藏  举报