BZOJ 3090: Coci2009 [podjela]

来源:互联网 发布:linux多线程服务器 编辑:程序博客网 时间:2024/05/07 16:22

描述

n 个农民, 他们住在 n 个不同的村子里,这 n 个村子形成一棵树。每个农民初始时获得 X 的钱,每一次操作, 一个农民可以从它自己的钱中, 取出任意数量的钱, 交给某个相邻村子的农民。对于每个农民给定一个值 vi, 问最少需要多少次操作, 使得每个农民最终拿到的钱 >= 给定的值。

约定:n2000, X104,一定有解。

Sol

(这道题不像它的通过人数显示出来的那么难。。。)

注意到一个重要的事实:每条树边连接的两个村民之间至多进行一次操作,证明可以用归(xian)纳(ran)法。

这样的话就可以考虑树上DP,原问题要最小化操作次数,可能会想当然地列一个 f[x][i] 表示 x 为根的子树,进行一系列操作之后根节点可以给出/必须接受 i 元的最小操作次数,这样当然也能正确转移,只不过时空复杂度均无法承受。

只需要从上文的陷阱中跳出来,我们发现操作次数是 O(n) 的,那就可以考虑设 f[x][i] 表示 x 为根的子树,操作 i 次之后,根节点最多可以给出多少钱(如果这个值为负,那就代表必须要接受多少钱)。

来看转移,这个DP的决策为每条边是否进行操作,对每个点 x 依次考虑它的每个孩子,对于一条父子边 (x,y),有:

  1. 这条边加入图中 f[x][i]+f[y][j]f[x][i+j+1]
  2. 如果 f[y][j]0,那么可以不要这条边,让树从这里断开:f[x][i]f[x][i+j]

而答案即为最小的 i 使得 f[root][i]0

稍有常识的人就能看出,这是一个背包的模型,而且 x 为根的子树的背包大小恰为 size[x],根据树上背包按 size 合并的理论,不难得到复杂度 O(n2)

Code

(感觉没有Code根本没人看。。。)

#include <bits/stdc++.h>#define rep(i, x, y) for (int i = (x), _ = (y); i <= _; ++i)#define down(i, x, y) for (int i = (x), _ = (y); i >= _; --i)#define x first#define y second#define mp make_pair#define pb push_back#define bin(x) (1<<(x))using namespace std;typedef long long LL;typedef pair<int, int> pii;template<typename T> inline void upmax(T &x, T y) { x < y ? x = y : 0; }template<typename T> inline void upmin(T &x, T y) { x > y ? x = y : 0; }template<typename T>inline void read(T &x) {    char c;    while ((c = getchar()) < '0' || c > '9');    for (x = c - '0'; (c = getchar()) >= '0' && c <= '9'; x = x * 10 + c - '0');}const int inf = 0x3f3f3f3f;const int N = 2005;vector<int> adj[N];int n, X, val[N];int size[N], f[N][N];void treeDP(int x, int fa) {    size[x] = 1;    rep (i, 0, adj[x].size()-1) {        if (adj[x][i] != fa) {            treeDP(adj[x][i], x);            size[x] += size[adj[x][i]];        }    }    static int g[2][N];    int t = 0, tot = 0;    rep (i, 0, tot)  g[t][i] = -inf;    g[t][0] = val[x];    rep (i, 0, adj[x].size()-1) {        int to = adj[x][i];        if (to != fa) {            rep (i, 0, tot+size[to])  g[t ^ 1][i] = -inf;            rep (j, 0, size[to]-1) {                int v = f[to][j];                rep (i, 0, tot) {                    upmax(g[t ^ 1][i + j + 1], g[t][i] + v);                }            }            int mi = 0;            while (mi < size[to] && f[to][mi] < 0) ++mi;            if (mi < size[to]) {                rep (i, 0, tot) upmax(g[t ^ 1][i + mi], g[t][i]);            }            tot += size[to];            t ^= 1;        }    }    rep (i, 0, tot) f[x][i] = g[t][i];}int main() {#ifdef LX_JUDGE    freopen("in.txt", "r", stdin);#endif    read(n), read(X);    int x, y;    rep (i, 1, n) {        read(val[i]);        val[i] = X - val[i];    }    rep (i, 2, n) {        read(x), read(y);        adj[x].pb(y);        adj[y].pb(x);    }    treeDP(1, 0);    int cur = 0;    while (f[1][cur] < 0) ++cur;    cout << cur << endl;    return 0;}
1 0
原创粉丝点击