【UOJ #105】【APIO2014】Beads and wires

http://uoj.ac/problem/105
好神的dp啊。
确定一个点为根之后,蓝线只能是竖着的,不能横跨兄弟。
枚举每个点为根进行树形dp是\(O(n^2)\)的,\(f(x,0/1)\)表示以\(x\)为根的子树中\(x\)是否作为蓝线终点的最大值。
更科学的做法:\(O(1)\)把根从一个father转移到它的son。
需要维护\(f(father,1)\)的最大和次大(防止son作为最大转移到father),利用father的信息更新\(f(son,0)\)\(f(son,1)\)的最大和次大(这里的换根不是真正把根换到son,只是说换根后son作为根的信息是正确的,不需要修改father的信息)。
时间复杂度\(O(n)\)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 200003;

struct node {int nxt, to, w;} E[N << 1];
int cnt = 0, point[N], f[N][2], fs[N][2];

void ins(int u, int v, int w) {E[++cnt] = (node) {point[u], v, w}; point[u] = cnt;}

int n, son[N], tot, fadis[N];

void dfs(int x, int fa) {
    for (int i = point[x]; i; i = E[i].nxt)
        if (E[i].to != fa) fadis[E[i].to] = E[i].w, dfs(E[i].to, x);
    
    tot = 0;
    for (int i = point[x]; i; i = E[i].nxt)
        if (E[i].to != fa) son[++tot] = E[i].to;
    
    for (int i = 1; i <= tot; ++i)
        f[x][0] += max(f[son[i]][0], f[son[i]][1]);
    
    int mx = -0x7fffffff, mxs = -0x7fffffff, num;
    for (int i = 1; i <= tot; ++i) {
        num = f[son[i]][0] + fadis[son[i]] - max(f[son[i]][0], f[son[i]][1]);
        if (num >= mx) mxs = mx, mx = num;
        else if (num > mxs) mxs = num;
    }
    
    f[x][1] = mx + fadis[x] + f[x][0];
    fs[x][1] = mxs + fadis[x] + f[x][0];
}

void move(int x, int y, int d) {
    int t, fx0 = f[x][0], fx1 = f[x][1], fxs1 = fs[x][1];
    if (fx1 == fx0 - max(f[y][1], f[y][0]) + f[y][0] + fadis[y]) fx1 = fxs1;
    fx0 -= max(f[y][1], f[y][0]);
    fx1 -= max(f[y][1], f[y][0]);
    fx1 += d;
    f[y][0] += max(fx0, fx1);
    f[y][1] += max(fx0, fx1);
    fs[y][1] += max(fx0, fx1);
    f[y][1] -= d; fs[y][1] -= d;
    if ((t = f[y][0] - max(fx0, fx1) + fx0 + d) >= f[y][1])
        fs[y][1] = f[y][1], f[y][1] = t;
    else if (t > fs[y][1]) fs[y][1] = t;
}

int ans = 0;

void dfsmove(int x, int fa) {
    ans = max(ans, f[x][0]);
    for (int i = point[x]; i; i = E[i].nxt) {
        int v = E[i].to;
        if (v == fa) continue;
        move(x, v, E[i].w);
        dfsmove(v, x);
    }
}

int main() {
    scanf("%d", &n);
    int u, v, e;
    for (int i = 1; i < n; ++i) {
        scanf("%d%d%d", &u, &v, &e);
        ins(u, v, e);
        ins(v, u, e);
    }
    
    dfs(1, 0);
    dfsmove(1, 0);
    printf("%d\n", ans);
    
    return 0;
}

转载于:https://www.cnblogs.com/abclzr/p/6728152.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值