题解-POI2014 FAR-FarmCraft

Problem

bzoj权限题,洛谷上可提交

洛谷上的奇葩翻译不要看,很多条件缺漏

题意简述:给定一棵树,每条边权为1,给定所有点点权,每条边仅能走两次,求以一定顺序遍历整棵树后,使所有点中的到达时间加点权的和的最大值最小(到达了就开始安装程序,点权即为安装时间,求最早什么时候所有电脑安装完毕)

Solution

前置技能:树形Dp、贪心

这一类(n)(1e5)范围的树上问题,一般都是树状Dp,考虑到题目中每条边仅能走两次,所以一旦到达了一个节点,一定会将以这个节点为根的子树全部遍历完再出子树,所以只用考虑到达一个节点后访问其儿子的先后顺序

考虑到遍历每棵子树的时间是一定的,即至少要花遍历整颗子树的时间,定义富余时间为遍历完某棵子树之后还需等待的时间,即子树下所有程序安装完的时间减去遍历时间,那么有一个贪心策略就是富余时间多的子树优先遍历,因为整棵树的遍历时间一定,所以富余部分多的优先解决

然后就没了,时间(O(nlog n)),本地测会爆栈,直接交就好了

Code

#include<bits/stdc++.h>
using namespace std;
#define rg register

template <typename _Tp> inline _Tp read(_Tp&x){
	char c11=getchar(),ob=0;x=0;
	while(c11^'-'&&!isdigit(c11))c11=getchar();if(c11=='-')ob=1,c11=getchar();
	while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;return x;
}

const int N=501000;
struct Edge{int v,nxt;}a[N<<1];
int head[N],f[N],g[N],stk[N];
int n,_;

template <typename _Tp> inline _Tp tmax(const _Tp A,const _Tp B){return A>B?A:B;}

inline void add(int u,int v){a[++_].v=v,a[_].nxt=head[u],head[u]=_;return ;}

inline int cmp(const int&A,const int&B){return f[A]-g[A]>f[B]-g[B];}

inline void dfs(int x,int fa){
	for(int i=head[x];i;i=a[i].nxt)
		if(a[i].v!=fa)
			dfs(a[i].v,x);
	int top=0;
	for(rg int i=head[x];i;i=a[i].nxt)
		if(a[i].v!=fa){
			g[a[i].v]+=2;
			f[a[i].v]=tmax(g[a[i].v],f[a[i].v]+1);
			stk[++top]=a[i].v;
		}
	sort(stk+1,stk+top+1,cmp);
	for(rg int i=1;i<=top;++i){
		f[x]=tmax(f[x],g[x]+f[stk[i]]);
		g[x]+=g[stk[i]];
	}return ;
}

int main(){
	read(n);
	for(rg int i=1;i<=n;++i)read(f[i]);int vas=f[1];
	for(rg int i=1,x,y;i<n;++i)read(x),read(y),add(x,y),add(y,x);
	dfs(1,0);printf("%d
",tmax(f[1],g[1]+vas));return 0;
}
原文地址:https://www.cnblogs.com/penth/p/9381235.html