zoukankan      html  css  js  c++  java
  • P4383 [八省联考2018]林克卡特树lct 树形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
    ", B + ans * k);
    	return 0;
    
    }
    
  • 相关阅读:
    成功破解校园网锐捷客户端,实现笔记本无线网卡wifi
    献给正在郁闷的人们
    用友客户通,无法打开登录 'turbocrm' 中请求的数据库。登录失败。
    如何得到cxgrid的当前编辑值
    cxgrid当底层数据集为空时显示一条空记录
    使用nlite将SCSI RAID 阵列驱动整合到系统安装光盘内
    开始菜单的运行没有了
    Delphi代码获取网卡物理地址三种方法
    登录用友通模块时提示:运行时错误'430',类不支持自动化或不支持期望的接口 ...
    CentOS7下安装MySQL Mr
  • 原文地址:https://www.cnblogs.com/olinr/p/10596348.html
Copyright © 2011-2022 走看看