P3574 [POI2014]FAR-FarmCraft 树上DP

题意:

给定一棵大小为(n)树,走过每条边需要花费(1)时间,安装软件又需要花费(c_i)时间,需要遍历整棵树并回到起点,想让所有点中到达时间+安装时间的最大值最小,问这个值是多少

范围&性质:(1le nle 5 imes10^5,1le c_ile 10^9)

分析:

我们对于节点(u)的儿子,按照最优方案下安装时间排序,我们二分一个最优时间,对于这些点我们从大往小的加入队列中,若当前的点无法直接插入到队列末尾,那就将他插入到最后一个元素的前面,这样能保证他影响到的点最少,要注意最后将(1)点得到的方案,和(c_1+(n-1)*2)取一次(max)

代码:

#include<bits/stdc++.h>

using namespace std;

namespace zzc
{
	const int maxn = 5e5+5;
	const long long inf = 1e15+5; 
	long long f[maxn],head[maxn],siz[maxn],c[maxn];
    int n,cnt=0,tot;
	
	struct edge
	{
		int to,nxt;
	}e[maxn<<1];
	
	struct node
	{
		int f,t;
		bool operator<(const node &b)const
		{
			return f>b.f;
		}
	}p[maxn]; 
	
	void add(int u,int v)
	{
		e[++cnt].to=v;
		e[cnt].nxt=head[u];
		head[u]=cnt;
	}
	
	bool check(long long x)
	{
		int i,ed=0;
		long long last=0;
		for(int i=1;i<=tot;i++)
		{
			if(last+p[i].f<=x) last+=p[i].t,ed=i;
			else if(last-p[ed].t+p[i].t+p[ed].f>x) return false;
			else last+=p[i].t;
		}
		return true;
	}
	
	void dfs(int u,int fa)
	{
		siz[u]=1;
		for(int i=head[u];i;i=e[i].nxt)
		{
			int v=e[i].to;
			if(v==fa) continue;
			dfs(v,u);
			siz[u]+=siz[v];
		}
		tot=0;
		for(int i=head[u];i;i=e[i].nxt)
		{
			int v=e[i].to;
			if(v==fa) continue;
			p[++tot].f=f[v];
			p[tot].t=siz[v]*2ll;
		}
		sort(p+1,p+tot+1);
		long long l=p[1].f,r=inf,mid;
		while(l<r)
		{
			mid=(l+r)>>1;
			if(check(mid)) r=mid;
			else l=mid+1;
		}
		if(fa) f[u]=c[u];
		else f[u]=c[u]+((n-1)<<1);
		if(tot&&f[u]<l+1) f[u]=l+1;
	}
	
	void work()
	{
		int a,b;
		scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%lld",&c[i]);
		for(int i=1;i<n;i++)
		{
			scanf("%d%d",&a,&b);
			add(a,b);add(b,a);
		}
		dfs(1,0);
		printf("%lld
",f[1]);
	}
	
}

int main()
{
	zzc::work();
	return 0;
}
原文地址:https://www.cnblogs.com/youth518/p/13716080.html