[八省联考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;
}
posted @ 2021-05-11 19:27  zkdxl  阅读(56)  评论(1编辑  收藏  举报