[BZOJ5252][八省联考2018]林克卡特树lct
description
在树上选出\(k\)条点不相交的链,求最大权值。
一个点也算是一条退化的链,其权值为\(0\)。
sol
别问我为什么现在才写这题
首先可以有一个很显然的\(O(nk)\)的\(dp\)。
设\(f_{i,j,0/1/2}\)表示\(i\)为根的子树中选出了\(j\)条链,此时\(i\)点的度数为\(0/1/2\)的最大权值。
然后你会发现这种选\(k\)个东西最大化收益/最小化代价的函数都会是凸的。
然后就凸优化一下?
然后就做完啦?
code
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
#define ll long long
const int N = 3e5+5;
const ll inf = 1ll<<60;
struct info{
ll f;int k;
bool operator < (const info &b) const
{return f==b.f?k>b.k:f<b.f;}
info operator + (const info &b) const
{return (info){f+b.f,k+b.k};}
}dp[N][3],ans;
int n,k,to[N<<1],nxt[N<<1],ww[N<<1],head[N],cnt;
void link(int u,int v,int w){
to[++cnt]=v;nxt[cnt]=head[u];ww[cnt]=w;head[u]=cnt;
}
inline void upt(info &x,info y){if(x<y)x=y;}
void dfs(int u,int f,ll c){
dp[u][0]=(info){0,0};dp[u][1]=(info){-c,1};dp[u][2]=(info){-inf,0};
for (int e=head[u];e;e=nxt[e])
if (to[e]!=f){
int v=to[e];dfs(v,u,c);
info tmp=max(dp[v][0],max(dp[v][1],dp[v][2]));
upt(dp[u][2],dp[u][2]+tmp);
upt(dp[u][2],dp[u][1]+dp[v][1]+(info){ww[e]+c,-1});
upt(dp[u][1],dp[u][1]+tmp);
upt(dp[u][1],dp[u][0]+dp[v][1]+(info){ww[e],0});
upt(dp[u][1],dp[u][0]+dp[v][0]+(info){-c,1});
upt(dp[u][0],dp[u][0]+tmp);
}
}
void solve(ll c){
dfs(1,0,c);ans=(info){-inf,0};
for (int i=1;i<=n;++i)
upt(ans,max(dp[i][0],max(dp[i][1],dp[i][2])));
}
int main(){
n=gi();k=gi()+1;ll L=0,R=0;
for (int i=1;i<n;++i){
int u=gi(),v=gi(),w=gi();
link(u,v,w);link(v,u,w);
L-=abs(w);R+=abs(w);
}
while (L<R){
ll mid=(L+R)>>1;solve(mid);
if (ans.k<=k) R=mid;else L=mid+1;
}
solve(R);printf("%lld\n",ans.f+R*k);
return 0;
}