P4383 [八省联考 2018] 林克卡特树(wqs二分、树形dp)

解析

请添加图片描述
它还真的不难。
乐。

这题没做出来有些谔谔。
外层wqs二分显而易见,里面不知道为啥我总觉得这个题可以贪心。
然后一直试图在原树直径上下功夫,一筹莫展。
看到题解“dp”两个字这题也就做完了…
就相当于要把一棵树分成若干条无交链,每分条需要一定代价,最大化价值和。
记录以下每个点向儿子连几条边以及是否和父亲连边之类的即可。
pair用于wqs二分的dp是真香)

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
using namespace std;

const int N=6e5+100;
const ll inf=3e11;

inline ll read(){
	ll x(0),f(1);char c=getchar();
	while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}

int n,m,k;

struct node{
	int to,nxt,w;
}p[N<<1];
int fi[N],cnt;
inline void addline(int x,int y,int w){
	p[++cnt]=(node){y,fi[x],w};fi[x]=cnt;
}
#define pr pair<ll,ll>
#define mkp make_pair
pr operator + (pr a,pr b){return mkp(a.first+b.first,a.second+b.second);}
pr operator + (pr a,ll b){return mkp(a.first+b,a.second);}
pr dp[3][N];
ll w;
void dfs(int x,int fa){
	dp[0][x]=mkp(0,0);
	dp[1][x]=dp[2][x]=mkp(-inf,0);
	for(int i=fi[x];~i;i=p[i].nxt){
		int to=p[i].to;
		if(to==fa) continue;
		dfs(to,x);
		dp[2][x]=max(dp[2][x],max(dp[2][x]+dp[0][to],(dp[1][x]+dp[1][to])+p[i].w));
		dp[1][x]=max(dp[1][x],max(dp[1][x]+dp[0][to],(dp[0][x]+dp[1][to])+p[i].w));
		dp[0][x]=max(dp[0][x],dp[0][x]+dp[0][to]);
	}
	dp[1][x]=max(dp[1][x],dp[0][x]);
	dp[0][x]=max(dp[0][x],mkp(dp[0][x].first+w,dp[0][x].second+1));
	dp[0][x]=max(dp[0][x],mkp(dp[1][x].first+w,dp[1][x].second+1));
	dp[0][x]=max(dp[0][x],mkp(dp[2][x].first+w,dp[2][x].second+1));
	return;
}

signed main(){
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	memset(fi,-1,sizeof(fi));cnt=-1;
	n=read();m=read()+1;
	for(int i=1;i<n;i++){
		int x=read(),y=read(),w=read();
		addline(x,y,w);addline(y,x,w);
	}
	ll st=-inf,ed=inf;
	while(st<ed){
		ll mid=(st+ed)>>1;
		w=mid;
		dfs(1,0);
		if(dp[0][1].second>=m) ed=mid;
		else st=mid+1;
	}
	w=st;
	debug("w=%lld\n",w);
	dfs(1,0);
	printf("%lld\n",dp[0][1].first-m*w);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值