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\n", B + ans * k);
	return 0;

}
posted @ 2019-03-25 20:18  olinr  阅读(147)  评论(0编辑  收藏  举报