[八省联考2018]林克卡特树

CXLI.[八省联考2018]林克卡特树

一眼发现函数是凸的。然后思考发现直接一个树形DP就能进行二分的check:设 \(f_{i,0/1/2}\) 分别表示节点 \(i\),其中 \(i\) 未被选/是一条链的链顶/被一条链经过,然后直接DP就行。

为什么二分边界要开到 \(10^{12}\)

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,g[300100][3],head[300100],cnt;
ll f[300100][3],ip;//0:x is on nothing 1:x is currently on a path 2:x's path has been matched
struct node{int to,next,val;}edge[600100];
void ae(int u,int v,int w){
	edge[cnt].next=head[u],edge[cnt].to=v,edge[cnt].val=w,head[u]=cnt++;
	edge[cnt].next=head[v],edge[cnt].to=u,edge[cnt].val=w,head[v]=cnt++;
}
void trans(ll &F,int &G,ll ff,int gg){
	if(F<ff)F=ff,G=gg;
	else if(F==ff)G=min(G,gg);
}
void dfs(int x,int fa){
	f[x][0]=f[x][1]=f[x][2]=0,g[x][0]=g[x][1]=g[x][2]=0;
	for(int i=head[x],y,z;i!=-1;i=edge[i].next){
		y=edge[i].to,z=edge[i].val;
		if(y==fa)continue;
		dfs(y,x);
		
		f[x][2]+=f[y][2],g[x][2]+=g[y][2];
//		printf("%d,%d:%d,%d,%d,%d\n",x,y,f[x][1],f[y][1],z,-ip);
		trans(f[x][2],g[x][2],f[x][1]+f[y][1]+z-ip,g[x][1]+g[y][1]+1);
		
		f[x][1]+=f[y][2],g[x][1]+=g[y][2];
		
		trans(f[x][1],g[x][1],f[x][0]+f[y][1]+z,g[x][0]+g[y][1]);
		
		f[x][0]+=f[y][2],g[x][0]+=g[y][2];
	}
	trans(f[x][2],g[x][2],f[x][1]-ip,g[x][1]+1);
	trans(f[x][2],g[x][2],f[x][0],g[x][0]);
//	printf("%d:\n%d %d %d\n%d %d %d\n",x,f[x][0],f[x][1],f[x][2],g[x][0],g[x][1],g[x][2]);
}
int main(){
	scanf("%d%d",&n,&m),m++,memset(head,-1,sizeof(head));
	for(int i=1,x,y,z;i<n;i++)scanf("%d%d%d",&x,&y,&z),ae(x,y,z);
	ll l=-1e12,r=1e12;
	while(l<r){
		ip=(l+r)>>1;
//		printf("%d------------\n",ip); 
		dfs(1,0);
		if(g[1][2]<=m)r=ip;
		else l=ip+1;
	}
	ip=l,dfs(1,0);
	printf("%lld\n",f[1][2]+1ll*l*m);
	return 0;
}

posted @ 2021-03-31 15:19  Troverld  阅读(39)  评论(0编辑  收藏  举报