P4383-[八省联考2018]林克卡特树【wqs二分,树形dp】

正题

题目链接:https://www.luogu.com.cn/problem/P4383


题目大意

\(n\)个点的一棵树,要求删除\(k\)条边然后接上\(k\)条边权为\(0\)的边后形成的树上选择一对\((p,q)\)\(p\)走简单路径到\(q\)的权值和最大。

\(n,k\leq 3\times 10^5\)


解题思路

其实可以理解为选恰好\(k+1\)条不相交的路径(可以选择一个点)使得权值和最大,这样删除路径最顶部的那条边一定有方案构造。

因为是恰好选择,所以考虑\(wqs\)二分,给每条路径加上一个权值\(mid\),然后考虑用树形\(dp\)做就很简单了。设\(f_{i,0/1/2}\)表示\(i\)号点没有路径经过/在路径之间/作为路径顶部时子树内的最小权值,然后转移即可。

时间复杂度:\(O(n\log W)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=3e5+10;
struct edge{
	ll to,next,w;
}a[N<<1];
struct node{
	ll w,g;
}f[N][3];
ll n,k,tot,c,ls[N];
node operator+(node x,node y)
{return (node){x.w+y.w,x.g+y.g};}
node operator+(node x,ll y)
{return (node){x.w+y,x.g};}
node operator^(node x,ll y)
{return (node){x.w,x.g+y};}
node mx(node x,node y){
	if(x.w==y.w)return (x.g>y.g)?x:y;
	return (x.w>y.w)?x:y;
}
void addl(ll x,ll y,ll w){
	a[++tot].to=y;
	a[tot].next=ls[x];
	a[tot].w=w;ls[x]=tot;
	return;
}
void dfs(ll x,ll fa){
	f[x][0]=f[x][1]=(node){0,0};f[x][2]=(node){c,1};
	for(ll i=ls[x];i;i=a[i].next){
		ll y=a[i].to;
		if(y==fa)continue;dfs(y,x);
		f[x][2]=mx(f[x][2]+f[y][0],(f[x][1]+f[y][1]+a[i].w+c)^1);
		f[x][1]=mx(f[x][0]+f[y][1]+a[i].w,f[x][1]+f[y][0]);
		f[x][0]=f[x][0]+f[y][0];
	}
	f[x][0]=mx(f[x][0],mx((f[x][1]+c)^1,f[x][2]));
	return;
}
signed main()
{
	scanf("%lld%lld",&n,&k);
	ll sum=0;k++;
	for(ll i=1;i<n;i++){
		ll x,y,w;
		scanf("%lld%lld%lld",&x,&y,&w);
		addl(x,y,w);addl(y,x,w);sum+=abs(w);
	}
	ll l=-sum,r=sum;
	while(l<=r){
		ll mid=(l+r)>>1;
		c=mid;dfs(1,0);
		if(f[1][0].g<k)l=mid+1;
		else r=mid-1;
	}
	c=l;dfs(1,0);
	printf("%lld\n",f[1][0].w-c*k);
	return 0;
}
posted @ 2021-10-13 08:02  QuantAsk  阅读(38)  评论(0编辑  收藏  举报