BZOJ4987 Tree

Tree

Description

从前有棵树。
找出$k$个点$A_1,A_2,…,A_k$。
使得$\sum_{1\leq i \leq k-1}dis(A_i,A_{i+1})$最小。

Input

第一行两个正整数$n,k$,表示树的顶点数和需要选出的点个数。
接下来$n-1$行每行$3$个非负整数$x,y,z$,表示从存在一条从$x$到$y$权值为$z$的边。

Output

一行一个整数,表示最小的距离和。
 
 
题解:
不难发现,选取的$k$个点在原树上是一个联通子树,考虑树形DP。
最终选取的$k$个点一定是从其中最远点对的一个点沿着直径同时遍历接在直径上的每一棵子树来到另一个点。
 
但是其实我们可以完全不考虑那么多,
就只需要把它想成沿着一个点出发遍历其他的点最终不需回到原来的点即可。
 
于是我们的状态就很显然,对于每一个节点$x$:
$C[x][m]$表示,起点和终点为$x$所在子树的任意一点,途径$x$所在的子树,共计遍历了$m$个不同点的最小代价。
$F[x][m]$表示,起点和终点中有一点为$x$所在子树的任意一点且另一点为$x$,途径$x$所在的子树,共计遍历了$m$各不同点的最小代价。
$B[x][m]$表示,起点和终点均为$x$,途径$x$所在的子树,共计遍历了$m$个不同的点的最小代价。
 
转移就很好写,DFS时,从大到小枚举一个点的前若干个子树大小之和,同时从大到小枚举当前子树大小,更新当前根节点的答案即可。
 
这样的复杂度相当于在以$x$为根的子树中枚举点对,且这两个点分别在不同儿子的子树中。因此,对于任意点对$(u,v)$,当且仅当DFS到$lca(u,v)$时会被枚举到,因此时间复杂度综合为$O(n^2)$。
 
 
 
 
AC代码如下
 
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M 3020
using namespace std;
int read(){
	int nm=0,fh=1;char cw=getchar();
	for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
	return nm*fh;
}
int n,m,fs[M],nt[M<<1],to[M<<1],len[M<<1];
int ans,F[M][M],B[M][M],C[M][M],dst,tmp,u,v,sz[M];
void update(int &x,int y){x=min(x,y);}
void link(int x,int y){nt[tmp]=fs[x],fs[x]=tmp,to[tmp]=y,len[tmp++]=dst;}
void DP(int x,int last){
	sz[x]=1,F[x][1]=C[x][1]=B[x][1]=0;
	for(int i=fs[x];i!=-1;i=nt[i]){
		if(to[i]==last) continue;
		int v=to[i]; DP(v,x);
		for(int k=sz[x];k>0;k--){
		    for(int j=sz[v];j>0;j--){
				update(B[x][j+k],B[v][j]+B[x][k]+(len[i]<<1));
				update(F[x][j+k],min(B[v][j]+F[x][k]+len[i],F[v][j]+B[x][k])+len[i]);
				update(C[x][j+k],min(min(C[x][k]+B[v][j],C[v][j]+B[x][k])+len[i],F[x][k]+F[v][j])+len[i]);
			}
		}
		sz[x]+=sz[v];
	}
	update(ans,min(min(C[x][m],B[x][m]),F[x][m]));
}
int main(){
	n=read(),m=read(),memset(&ans,0x7f,sizeof(int));
	memset(fs,-1,sizeof(fs)),memset(F,0x7f,sizeof(F));
	memset(C,0x7f,sizeof(C)),memset(B,0x7f,sizeof(B));
	for(int i=1;i<n;i++) u=read(),v=read(),dst=read(),link(u,v),link(v,u);
	DP(1,0),printf("%d\n",ans);
	return 0;
}
 
posted @ 2018-08-06 18:50  OYJason  阅读(308)  评论(0编辑  收藏  举报