牛客 Minieye杯第十五届华中科技大学程序设计邀请赛网络赛 I-Tree(二分+树形dp)牛客 Minieye杯第十五届华中科技大学程序设计邀请赛网络赛 I-Tree(二分+树形dp)
题意:一棵树有n个节点,每个节点有一个值,把n个节点分成k个连通的部分,使得k个部分的和的最小值最大。
思路:二分枚举答案,树形dp检查。对于一个节点v,如果他和他的子树的值小于枚举的答案
(dp[v]<x),那么他和他的子树肯定要加在他的父亲u上(dp[u]+=dp[v]);如果dp[v]>=x,则把他标记,他和他的子树形成一个连通部分。如果能形成部分大于等于k个,那肯定能由他更新到答案,所以这里的ans不用取max。
ps:坤神tql,3分钟出思路,5分钟写完ac。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#define ll long long
using namespace std;
const int N=1e5+10;
vector<int> g[N];
int n,k;
int w[N],num;
bool vis[N];
ll dp[N];
void dfs(int u,int fa,ll x)
{
	for(int i=0;i<g[u].size();i++)
	{
		int v=g[u][i];
		if(vis[v]||v==fa) continue;
		dfs(v,u,x);
		if(dp[v]<x)
			dp[u]+=dp[v];
	}
	//如果大于枚举的答案了,直接标记 
	if(dp[u]>=x)
	{
		vis[u]=1;
		num++;
	}	
}
bool judge(ll x)
{
	for(int i=1;i<=n;i++)
	{
		dp[i]=w[i];
		vis[i]=0;
	}	
	num=0;
	dfs(1,0,x);
	if(num>=k) return 1;
    else return 0;	
}
int main(void)
{
	int u,v;
	scanf("%d%d",&n,&k);
	for(int i=1;i<n;i++)
	{
		scanf("%d%d",&u,&v);
		g[u].push_back(v);
		g[v].push_back(u);
	}
	for(int i=1;i<=n;i++) 
		scanf("%d",&w[i]);
	ll l=0,r=2e10,ans=0;
	while(l<=r)
	{
		ll mid=(l+r)/2;
		if(judge(mid))
		{
			//直接更新答案 
			ans=mid;
			l=mid+1;	
		}		
		else
		{
			r=mid-1;	
		}	
	}
	printf("%lld\n",ans);
	
	return 0;	
} 

相关文章:

  • 2021-08-25
  • 2021-12-14
  • 2021-05-23
  • 2021-07-25
  • 2022-12-23
  • 2019-07-04
  • 2022-12-23
猜你喜欢
  • 2021-07-30
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-10-09
  • 2021-12-09
  • 2021-10-15
相关资源
相似解决方案