Tree 2020杭电hdu多校第5场1007

树形DP裸题,不知道为啥过得不是很多,可能都被前期题卡住了没看这题

sumkj1[v]表示以v为根节点选k-1个子节点并且全部满足度数不超过k的最大连通块的边权和

sum[v]=sumkj1[v]+e[u][v].l,也就是包含一条连向父亲的节点

这个用一遍dfs就能处理出来,每次排个序取最大的k-1个子节点

第二遍dfs求把父节点fa当做子树到u的满足条件的最大连通块的边权和存到u所连向的点的g[v,l]中,其中 l 表示v子树的最大值

然后我们假设把u当那个超过k的,也就是把g[u]中所有的子节点的l加起来,更新一下最大值

最后枚举u的每个子节点v,算出对应的nfmx,也就是v把u当子节点的满足条件的最大边权和,传递给v

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxl=2e5+10;

int n,k;
ll ans;
struct ed
{
	int to;ll l;
	bool operator < (const ed &b)const
	{
		return l>b.l;
	}
};
vector<ed> e[maxl],g[maxl];
ll sumkj1[maxl],sum[maxl];

inline void prework()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
		e[i].clear(),g[i].clear();
	int u,v,l;
	for(int i=1;i<=n-1;i++)
	{
		scanf("%d%d%d",&u,&v,&l);
		e[u].push_back(ed{v,l});
		e[v].push_back(ed{u,l});
	}
	
}

inline void dfs1(int u,int fa)
{
	int v;
	for(ed ee:e[u])
	{
		v=ee.to;
		if(v==fa) continue;
		dfs1(v,u);
		sum[v]=ee.l+sumkj1[v];
		g[u].push_back(ed{v,sum[v]});
	}
	sort(g[u].begin(),g[u].end());
	int len=g[u].size();len=min(len,k-1);
	sumkj1[u]=0;
	for(int i=0;i<len;i++)
		sumkj1[u]+=g[u][i].l;
}

inline void dfs2(int u,int fa,ll fmx)
{
	if(fa!=0)
		g[u].push_back(ed{fa,fmx});
	sort(g[u].begin(),g[u].end());
	int v,len=g[u].size(),up=min(len,k);
	ll tmp=0,nfmx;
	for(int i=0;i<len;i++)
		tmp+=g[u][i].l;
	ans=max(ans,tmp);
	tmp=0;
	for(int i=0;i<up;i++)
		tmp+=g[u][i].l;
	for(int i=0;i<len;i++)
	{
		v=g[u][i].to;
		if(v==fa) continue;
		if(i<up)
			nfmx=tmp-sum[v]+(sum[v]-sumkj1[v]);
		else
			nfmx=tmp-g[u][k-1].l+(sum[v]-sumkj1[v]);
		dfs2(v,u,nfmx);
	}
}

inline void mainwork()
{
	ans=0;
	if(k==0) return;
	dfs1(1,0);
	dfs2(1,0,0);
}

inline void print()
{
	printf("%lld\n",ans);
}

int main()
{
	int t;
	scanf("%d",&t);
	for(int i=1;i<=t;i++)
	{
		prework();
		mainwork();
		print();
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/liufengwei1/article/details/107790493