题解-八省联考2018 林克卡特树

我们先来转换一下题意,即为选 (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
",f[1][0].fi+res*m);
	return 0;
}
原文地址:https://www.cnblogs.com/zcr-blog/p/15045699.html