码迷,mamicode.com
首页 > 其他好文 > 详细

P4383 [八省联考2018]林克卡特树lct 树形DP+凸优化/带权二分

时间:2019-03-25 20:45:18      阅读:179      评论:0      收藏:0      [点我收藏+]

标签:null   ons   friend   int   详细   简单   span   测试   树形dp   

$ \color{#0066ff}{ 题目描述 }$

小L 最近沉迷于塞尔达传说:荒野之息(The Legend of Zelda: Breath of The Wild)无法自拔,他尤其喜欢游戏中的迷你挑战。

游戏中有一个叫做“LCT” 的挑战,它的规则是这样子的:现在有一个N 个点的 树(Tree),每条边有一个整数边权vi ,若vi >= 0,表示走这条边会获得vi 的收益;若vi < 0 ,则表示走这条边需要支付- vi 的过路费。小L 需要控制主角Link 切掉(Cut)树上的 恰好K 条边,然后再连接 K 条边权为 0 的边,得到一棵新的树。接着,他会选择树上的两个点p; q ,并沿着树上连接这两点的简单路径从p 走到q ,并为经过的每条边支付过路费/ 获取相应收益。

海拉鲁大陆之神TemporaryDO 想考验一下Link。他告诉Link,如果Link 能切掉 合适的边、选择合适的路径从而使 总收益 - 总过路费最大化的话,就把传说中的大师之剑送给他。

小 L 想得到大师之剑,于是他找到了你来帮忙,请你告诉他,Link 能得到的 总收益 - 总过路费最大是多少。

\(\color{#0066ff}{输入格式}\)

从文件lct.in 中读入数据。

输入第一行包含两个正整数N; K,保证0 <= K < N <= 3*\(10^5\)

接下来N - 1 行,每行包含三个整数xi; yi; vi,表示第i 条边连接图中的xi; yi 两点, 它的边权为vi。

\(\color{#0066ff}{输出格式}\)

输出到文件lct.out 中。

输出一行一个整数,表示答案。

\(\color{#0066ff}{输入样例}\)

5 1
1 2 3
2 3 5
2 4 -3
4 5 6

\(\color{#0066ff}{输出样例}\)

14

\(\color{#0066ff}{数据范围与提示}\)

【样例1 解释】

一种可能的最优方案为:切掉(2; 4; ?3) 这条边,连接(3; 4; 0) 这条边,选择(p; q) = (1; 5)。

? 对于10% 的数据,k = 0 ;

? 对于另外10% 的数据,k = 1 ;

? 对于另外15% 的数据,k = 2 ;

? 对于另外25% 的数据,k <= 100 ;

? 对于其他数据,没有特殊约定。

对于全部的测试数据,保证有1 <= N <= 3 * \(10^5\); 1 <= xi; yi <= N; |vi| <= \(10^6\)

【提示】

题目并不难。

\(\color{#0066ff}{题解}\)

带权二分,这篇题解写的比较详细,转载自 EternalAlexander

技术图片技术图片

技术图片

#include<bits/stdc++.h>
#define LL long long
LL in() {
    char ch; LL x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
const int maxn = 3e5 + 100;
struct node {
    LL mx, k;
    node(LL mx = 0, LL k = 0): mx(mx), k(k) {}
    friend node operator + (const node &a, const node &b) { return node(a.mx + b.mx, a.k + b.k); }
    friend bool operator < (const node &a, const node &b) { return (a.mx < b.mx || (a.mx == b.mx && a.k < b.k)); }
}f[maxn][3];
struct EDGE {
    int to; LL dis;
    EDGE *nxt;
    EDGE(int to = 0, LL dis = 0, EDGE *nxt = NULL): to(to), dis(dis), nxt(nxt) {}
};
EDGE *head[maxn];
LL n, k, mid, ans, B;

void dfs(int x, int fa) {
    f[x][0] = f[x][1] = node(0, 0);
    f[x][2] = node(-mid, 1);
    for(EDGE *i = head[x]; i; i = i->nxt) {
        if(i->to == fa) continue;

        dfs(i->to, x);
        f[x][2] = std::max(f[x][2] + f[i->to][0], f[x][1] + f[i->to][1] + node(i->dis - mid, 1));
        f[x][1] = std::max(f[x][1] + f[i->to][0], f[x][0] + f[i->to][1] + node(i->dis, 0));
        f[x][0] = f[x][0] + f[i->to][0];
    }
    f[x][0] = std::max(f[x][0], std::max(f[x][1] + node(-mid, 1), f[x][2]));
}

void add(int from, int to, int dis) { head[from] = new EDGE(to, dis, head[from]); }
LL ok() {
    dfs(1, 0);
    B = f[1][0].mx;
    return f[1][0].k;
}
int main() {
    n = in(), k = in() + 1;
    LL l, r = 0;
    LL x, y, z;
    for(int i = 1; i < n; i++) {
        x = in(), y = in(), z = in();
        add(x, y, z), add(y, x, z);
        r += z > 0? z : -z;
    }
    l = -r;
    while(l <= r) {
        mid = (l + r) >> 1;
        if(ok() >= k) l = mid + 1, ans = mid;
        else r = mid - 1;
    }
    mid = ans;
    ok();
    printf("%lld\n", B + ans * k);
    return 0;

}

P4383 [八省联考2018]林克卡特树lct 树形DP+凸优化/带权二分

标签:null   ons   friend   int   详细   简单   span   测试   树形dp   

原文地址:https://www.cnblogs.com/olinr/p/10596348.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!