zoukankan      html  css  js  c++  java
  • @loj


    @description@

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

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

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

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

    input
    输入第一行包含两个正整数 N, K,保证 (0 le K lt N le 3 imes 10^5)
    接下来 N − 1 行,每行包含三个整数 (x_i, y_i, v_i) ,表示第 i 条边连接图中的 (x_i, y_i) 两点,它的边权为 (v_i)。保证 (1 le x_i, y_i le N)(|v_i| le 10^6)
    output
    输出一行一个整数,表示答案。

    simple input
    5 1
    1 2 3
    2 3 5
    2 4 -3
    4 5 6
    simple output
    14
    simple explain
    一种可能的最优方案为:切掉 (2, 4, −3) 这条边,连接 (3, 4, 0) 这条边,选择 (p, q) = (1, 5)。

    提示:题目并不难。(这句话属于题目描述,不是我自己加的 qwq)

    @solution@

    那句 “题目并不难” 真的是出题人留的。

    @part - 1@

    先来转换一下题目的要求:恰好选择 K + 1 条不相交的路径(单个点算一条路径),使它们的权值和最大。可以发现这个问题与原问题是等价的。

    对个数的限制,通常可以使用带权二分解决。打表观察发现这道题具有带权二分所需要的凸性,所以我们的确可以用带权二分。
    我们给每一条路径二分一个附加权值,权值越大选的路径越多,权值越小选的路径越少。
    二分的上界为所有边权的最大值,即我选取任何边作为一条路径,都不如把它拆成两个点作为两条路径优秀,路径数量最大。
    但是二分的下界并不是所有边权的最小值,实测 -10^9 是可以的,但是理论应该是所有边权的和的相反数……
    管它的。调出来就行。

    @part - 2@

    定义 (dp[0/1][i]) 表示以结点 (i) 为根的子树中,所能得到的最优解。0 表示不选择 (i) 到父亲这条边,1 表示选择 (i) 到父亲这条边。再记录 (cnt[0/1][i]) 表示取得最优值时所对应的路径数量。

    基本思路是:我们仅在路径的顶端(即两个端点的 LCA)将这条路径的贡献计入答案。

    转移时维护 (temp[0/1]),表示当前根节点向下没有连一条路径所能取到最优解。
    这样 (dp[1][i]) 可以由 (temp + dp[1][j])(dp[0][j]) 两种方法转移过来,其中 j 是 i 的孩子。
    (dp[0][i]) 可以由 (dp[1][i] + dp[1][j])(dp[0][j]) 两种方法转移过来。

    细节较多,可以参考一下代码。因为我语文太差实在是描述不出来了QAQ。

    @accepted code@

    #include<cmath>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int MAXN = 300000;
    struct edge{
    	int to; ll dis;
    	edge *nxt;
    }edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt = &edges[0];
    void addedge(int u, int v, int w) {
    	edge *p = (++ecnt);
    	p->to = v, p->dis = w;
    	p->nxt = adj[u], adj[u] = p;
    	p = (++ecnt);
    	p->to = u, p->dis = w;
    	p->nxt = adj[v], adj[v] = p;
    }
    int N, K;
    int cnt[2][MAXN + 5];
    ll dp[2][MAXN + 5], ans, mid;
    void dfs(int rt, int pre) {
    	ll tmp1; int tmp2;
    	tmp1 = dp[0][rt] = dp[1][rt] = 0;
    	tmp2 = cnt[1][rt] = cnt[0][rt] = 0;
    	if( mid >= 0 )
    		dp[0][rt] = mid, cnt[0][rt] = 1;
    	for(edge *p=adj[rt];p!=NULL;p=p->nxt) {
    		if( p->to == pre ) continue;
    		dfs(p->to, rt);
    		
    		if( (dp[0][rt] + dp[0][p->to] > dp[1][rt] + dp[1][p->to] + p->dis + mid) || (dp[0][rt] + dp[0][p->to] == dp[1][rt] + dp[1][p->to] + p->dis + mid && cnt[0][rt] + cnt[0][p->to] > cnt[1][rt] + cnt[1][p->to] + 1) )
    			dp[0][rt] += dp[0][p->to], cnt[0][rt] += cnt[0][p->to];
    		else dp[0][rt] = dp[1][rt] + dp[1][p->to] + p->dis + mid, cnt[0][rt] = cnt[1][rt] + cnt[1][p->to] + 1;
    		
    		if( (dp[1][rt] + dp[0][p->to] > tmp1 + dp[1][p->to] + p->dis) || (dp[1][rt] + dp[0][p->to] == tmp1 + dp[1][p->to] + p->dis && cnt[1][rt] + cnt[0][p->to] > tmp2 + cnt[1][p->to]) )
    			dp[1][rt] += dp[0][p->to], cnt[1][rt] += cnt[0][p->to];
    		else dp[1][rt] = tmp1 + dp[1][p->to] + p->dis, cnt[1][rt] = tmp2 + cnt[1][p->to];
    		
    		tmp1 += dp[0][p->to], tmp2 += cnt[0][p->to];
    	}
    }
    bool check() {
    	dfs(1, 0); ans = dp[0][1];
    	return cnt[0][1] >= K + 1;
    }
    int main() {
    	scanf("%d%d", &N, &K);
    	for(int i=1;i<N;i++) {
    		int x, y, v;
    		scanf("%d%d%d", &x, &y, &v);
    		addedge(x, y, v);
    	}
    	ll le = ll(-1E9), ri = ll(1E6);
    	while( le < ri ) {
    		mid = ll(floor((le + ri)/2.0));
    		if( check() ) ri = mid;
    		else le = mid + 1;
    	}
    	mid = le; check();
    	printf("%lld
    ", ans - 1LL*le*(K + 1));
    }
    

    @details@

    这种树形 dp 口胡起来很简单,写起来是真的……不好写。

    找二分的下界让我找了好久……
    以后有时间可以去总结一下带权二分上下界的套路。

  • 相关阅读:
    CSS笔记
    EasyUI笔记
    EasyUI treegrid 获取编辑状态中某字段的值 [getEditor方法获取不到editor]
    2019.10.12解题报告
    %lld 和 %I64d
    关于kmp算法
    洛谷p2370yyy2015c01的U盘题解
    About me & 友链
    关于Tarjan
    洛谷p3398仓鼠找suger题解
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10215044.html
Copyright © 2011-2022 走看看