题意
如果脑袋不转弯,照着题意理解挺绕的,一点思路也没有。不过发现删边后加边权为\(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;
}