[八省联考2018] 林克卡特树
\(\text{Problem}:\)[八省联考2018] 林克卡特树
\(\text{Solution}:\)
考虑割 \(K\) 条边等价于将原树分为 \(K+1\) 个连通块,答案为 \(K+1\) 个连通块的最长链之和。那么题目转化为从原树上选出 \(K+1\) 条不相交的链,使得权值和最大。
设 \(f_{x,i,0/1/2}\) 表示 \(x\) 的子树中选了 \(i\) 条完整的链,结点 \(x\) 的度数为 \(0/1/2\) 的答案。设 \(y\) 为 \(x\) 的儿子结点,下面进行分类讨论(递归结束 \(y\) 后记 \(f_{y,i,0}=\max\{f_{y,i,0},f_{y,i-1,1},f_{y,i,2}\}\),即子树内最优解,其他定义不变):
- 结点 \(x\) 度数为 \(0\)。此时直接合并子树内最优解即可,有转移 \(f_{x,i,0}=\max\{f_{x,i,0},f_{x,j,0}+f_{y,i-j,0}\}\)。
- 结点 \(x\) 度数为 \(1\)。此时可以直接合并 \(y\) 的最优解,也可以选择 \(x\rightarrow y\) 这条边使得 \(y\) 的不完整链接到 \(x\) 上。有转移(\(val\) 指对应边权,下同):
\[f_{x,i,1}=\max\{f_{x,i,1},f_{x,j,1}+f_{y,i-j,0},f_{x,j,0}+f_{y,i-j,1}+val\}
\]
- 结点 \(x\) 度数为 \(2\)。此时可以直接合并 \(y\) 的最优解,也可以选择 \(x\rightarrow y\) 这条边,使得 \(y\)和 \(x\) 的不完整链合并为完整链。有转移:
\[f_{x,i,2}=\max\{f_{x,i,2},f_{x,j,2}+f_{y,i-j,0},f_{x,j,1}+f_{y,i-j-1,1}+val\}
\]
转移时注意边界。考虑初始状态,我们将单独一个点为链的情况看作环,于是有 \(f_{x,0,0}=f_{x,0,1}=f_{x,1,2}=0\)。时间复杂度 \(O(nk^{2})\)。
通过打表等方式,不难发现函数 \(f\) 是一个上凸包(感性证明即每次选择的链都是当前最优的,具体证明可以参考 Link),故考虑 \(\text{wqs}\) 二分优化 \(dp\)。时间复杂度优化为 \(O(n\log n)\),可以通过。
\(\text{Code}:\)
#include <bits/stdc++.h>
#pragma GCC optimize(3)
#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=300010;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
return s*w;
}
int n,K;
pair<int,int> f[N][3],W;
inline pair<int,int> operator + (pair<int,int> x,pair<int,int> y) { return mk(x.fi+y.fi,x.se+y.se); }
int head[N],maxE; struct Edge { int nxt,to,rdis; }e[N<<1];
inline void Add(int u,int v,int w) { e[++maxE].nxt=head[u]; head[u]=maxE; e[maxE].to=v; e[maxE].rdis=w; }
void DFS(int x,int fa)
{
f[x][0]=f[x][1]=mk(0,0), f[x][2]=W;
for(ri int i=head[x];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==fa) continue;
DFS(v,x);
f[x][2]=max(f[x][2]+f[v][0],f[x][1]+f[v][1]+mk(e[i].rdis,0)+W);
f[x][1]=max(f[x][1]+f[v][0],f[x][0]+f[v][1]+mk(e[i].rdis,0));
f[x][0]=f[x][0]+f[v][0];
}
f[x][0]=max(f[x][0],max(f[x][1]+W,f[x][2]));
}
inline int Check()
{
DFS(1,0);
return f[1][0].se;
}
signed main()
{
n=read(), K=read()+1;
for(ri int i=1;i<n;i++)
{
int u,v,w;
u=read(), v=read(), w=read();
Add(u,v,w), Add(v,u,w);
}
int L=-1e12, R=1e12;
int ans=0;
while(L<=R)
{
int mid=(L+R)/2;
W=mk(-mid,1);
if(Check()>=K) ans=f[1][0].fi-W.fi*K, L=mid+1;
else R=mid-1;
}
printf("%lld\n",ans);
return 0;
}
夜畔流离回,暗叹永无殿。
独隐万花翠,空寂亦难迁。
千秋孰能为,明灭常久见。
但得心未碎,踏遍九重天。