我们先来转换一下题意,即为选 \(k+1\) 条互不相交的链,使得权值和最大。估计没人和我一样选 \(k\) 条小于 \(0\) 的边变成 \(0\) 吧。
这个东西看起来就只能 dp 求,设 f[i,j,0/1/2] 代表以 i 为根的子树选出 j 条链,然后 i 不选,i 是一条链的顶,i 的子树中有一条穿过 i 的链。
然后转移就比较显然了,为了方便,我们最后合并信息到 \(f[i,j,0]\) 上。通过打表我们发现这是一个关于 j 的凸函数,所以可以 wqs 二分。根据数据范围也可以猜测。我们稍微改写一下 dp 方程即可。
然后我的写法又丑又难写还难调,于是我打开了题解,看到了下面这种写法,稍微学习了一下(就是用一个 pair 来存,然后重载运算符)。
#include<bits/stdc++.h>
#define pi pair<ll,int>
#define mp make_pair
#define fi first
#define se second
typedef long long ll;
using namespace std;
const int maxn=600005;
const ll inf=0x3f3f3f3f3f3f3f3f;
template<typename T>
void read(T &x){
T flag=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')flag=-1;
for(x=0;isdigit(ch);ch=getchar())x=x*10+ch-'0';
x*=flag;
}
int n,m;
int pre[maxn],to[maxn],val[maxn],head[maxn],tot=1;
pi f[maxn][3],I;
pi operator+(pi a,pi b){
return mp(a.fi+b.fi,a.se+b.se);
}
pi operator+(pi a,ll b){
return mp(a.fi+b,a.se);
}
bool operator<(pi a,pi b){
if(a.fi!=b.fi)return a.fi<b.fi;
return a.se<b.se;
}
void add_edge(int u,int v,int w){
pre[++tot]=head[u];to[tot]=v;val[tot]=w;head[u]=tot;
}
void dfs(int u,int fa){
f[u][0]=f[u][1]=f[u][2]=mp(0,0);
f[u][2]=max(f[u][2],I);
for(int i=head[u];i;i=pre[i]){
int v=to[i];
if(v==fa)continue;
dfs(v,u);
f[u][2]=max(f[u][2],max(f[u][2]+f[v][0],f[u][1]+f[v][1]+I+val[i]));
f[u][1]=max(f[u][1],max(f[u][0]+f[v][1]+val[i],f[u][1]+f[v][0]));
f[u][0]=max(f[u][0],f[u][0]+f[v][0]);
}
f[u][0]=max(f[u][0],max(f[u][1]+I,f[u][2]));
}
int main(){
read(n);read(m);m++;
for(int i=1,u,v,w;i<n;i++){
read(u);read(v);read(w);
add_edge(u,v,w);add_edge(v,u,w);
}
ll l=-1e12,r=1e12,res=0;
while(l<=r){
ll mid=(l+r)/2;
I=mp(-mid,1);
dfs(1,0);
int v=f[1][0].se;
if(v>=m){
res=mid;
l=mid+1;
}else{
r=mid-1;
}
}
I=mp(-res,1);
dfs(1,0);
printf("%lld\n",f[1][0].fi+res*m);
return 0;
}