题意

如果脑袋不转弯,照着题意理解挺绕的,一点思路也没有。不过发现删边后加边权为\(0\)的边似乎没什么意义。
其实题目的意思就是选\(k+1\)条链,使得链和最大。

思路

首先考虑\(O(nk)\)的dp。
\(dp[i][j][0/1/2]\):表示\(i\)子树内有\(j\)条链,\(i\)度数\(0/1/2\)
转移好想,不写了。
以链数为自变量,发现函数是凸性的。
所以wqs二分,再跑dp,保证第二关键字链数尽量小,直接dp状态内重载结构体存收益和链数,不过双倍常数,挺慢的(可这道题是10s,暴力也许都能冲过)。

code

点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
int nxt[N], to[N], head[N], len[N], ecnt, n, K;
ll mid;
void add_edge(int u, int v, int w) {nxt[++ecnt] = head[u]; to[ecnt] = v; len[ecnt] = w; head[u] = ecnt;}
struct rose {
	ll w; int c;
	bool operator < (const rose &u) const{return w == u.w ? c > u.c : w < u.w;}
	inline rose operator + (const rose &u) const{return (rose){u.w + w, u.c + c};}
	inline rose operator + (ll d) {return (rose){w + d, c};}
}dp[N][3];

rose Ins(rose u) {return (rose){u.w - mid, u.c + 1};}		//加一条链

void dfs(int u, int fa) {
	dp[u][2] = max(dp[u][2], (rose){-mid, 1});
	for(int i = head[u]; i; i = nxt[i]) {
		int v = to[i]; if(v == fa) continue;
		dfs(v, u);
		dp[u][2] = max(dp[u][2] + dp[v][0], Ins(dp[u][1] + dp[v][1] + len[i]));
		dp[u][1] = max(dp[u][1] + dp[v][0], dp[u][0] + dp[v][1] + len[i]);
		dp[u][0] = dp[u][0] + dp[v][0];
	}
	dp[u][0] = max(dp[u][0], max(Ins(dp[u][1]), dp[u][2]));
}

int main() {
	ll sum = 0;
	scanf("%d%d", &n, &K);
	for(int i = 1; i < n; i++) {int u, v, w; scanf("%d%d%d", &u, &v, &w); add_edge(u, v, w), add_edge(v, u, w); sum += abs(w);}
	dfs(1, 0);
	ll l = -sum, r = sum, bst;
	while(l <= r) {
		mid = (l + r) >> 1;
		memset(dp, 0, sizeof(dp));
		dfs(1, 0);
		if(dp[1][0].c <= K) {bst = dp[1][0].w + (K + 1) * mid; r = mid - 1;}
		else {l = mid + 1;}
	}
	printf("%lld", bst);
	return 0;
}