T解 BZOJ-4033 树形DP HAOI2015 T1

大家都很强, 可与之共勉。

题面:
有一棵点数为 N 的树,树边有边权。给你一个在 0~ N 之内的正整
数 K ,你要在这棵树中选择 K个点,将其染成黑色,并将其他 的
N-K个点染成白色 。 将所有点染色后,你会获得黑点两两之间的距
离加上白点两两之间的距离的和的受益。问受益最大值是多少。

其实就是:
有一棵N个点的边代权树,每个点都是白点,然后你要选择K个点将其染黑
一棵树的价值是白点两两间的距离和加黑点两两间的距离和,求最大化价值。

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

换句话说,i所考虑到的点的DFS序一定小于j所考虑到的点

于是我们就可以dpi,j表示子树i选了j个黑点,然后转移暴力枚举子树里选了几个黑点,复杂度还是O(n^2)的,关键在于如何去转移。

那么若不好转移,我们考虑设状态的问题。如果设他是子树i选了j个黑点,子树内的同色点对距离和的话,是不好转移的对吧……所以我们要把他们往子树外连的也考虑进来

我们可以这样做,假设我们把任意一对同色点之间的路径给标一下,那么dpi,j记录的就是,子树i选了j个黑点,子树内的所有被标过的路径权值和,这样就能转移了

一句话,就是改变一下状态定义,就能让他不仅考虑子树i内部的和,把子树外的延伸到子树内的那些边也考虑了。

#include "cstdio"
#include "cctype"
#include "cstring"
#include "algorithm"

#define abs(a)  ((a) > 0 ? (a) : -(a))
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))

#define rep(i, m, n)  for(register int i = (m); i <= (n); ++i)
#define res(i, m, n)  for(register int i = (m); i >= (n); --i)
#define edges(u) for(register int i = head[u]; i; i = g[i].pre)

template <class T>
inline bool readIn(T &x)  {
    T flag = 1;  char ch;
    while(!(isdigit(ch = (char) getchar())) && ch != EOF)  if( ch == '-' )  flag = -1;
    if(ch == EOF)  return false;
    for(x = ch - 48; isdigit(ch = (char) getchar()); x = (x << 1) + (x << 3) + ch - 48);
    x *= flag;
    return true;
}

template <class T>
inline void write(T x)  {
     if (x > 9)
     write(x / 10);
     putchar(x % 10 + 48);
}

template <class T>
inline bool writeIn(T x)  {
     if (x < 0)  {
         putchar('-');
         x = -x;
     }
     write(x);
}

typedef long long LL;

#define MAXN 2005

class edge  {
public:
    int to, pre, w;
    edge(int to = 0, int pre = 0, int w = 0) : to(to), pre(pre), w(w)  { }
} g[MAXN << 1];

int n, k, u, v, w, ne, head[MAXN], siz[MAXN];
LL dp[MAXN][MAXN];

inline bool adde(int u, int v, int w)  {
    g[++ne] = edge(v, head[u], w), head[u] = ne;
    return true;
}

inline void tree_dp(int u, int v, int w)  {
    res(i, siz[u], 0)  {
        rep(j, 1, siz[v])
            dp[u][i + j] = max(dp[u][i + j], dp[u][i] + dp[v][j] + (LL) j * (k-j) * w + (LL) (siz[v]-j) * (n - k - siz[v] + j) * w );
        dp[u][i] += dp[v][0] + (LL) siz[v] * (n - k - siz[v]) * w;
    }
} 

void dfs(int u, int f)  {
    siz[u] = 1;
    edges(u)  {
        int v = g[i].to;
        if(v ^ f)  {
            dfs(v, u);
            tree_dp(u, v, g[i].w);
            siz[u] += siz[v];
        }
    }
}

int main()  {
    readIn(n);readIn(k);
    rep(i, 1, n - 1)  readIn(u), readIn(v), readIn(w), adde(u, v, w), adde(v, u, w);
    dfs(1, 0);
    writeIn(dp[1][k]);
    putchar('\n');
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值