BZOJ3829 [Poi2014]FarmCraft 【树形dp】

题目链接

BZOJ3829

题解

(f[i])为从(i)父亲进入(i)之前开始计时,(i)的子树中最晚装好的时间
同时记(siz[i])为节点(i)子树大小的两倍,即为从父亲进入并回到父亲的时间
那么有

[f[i] = max{C[i],f[to] + siz_{pre}} + 1 ]

我们只需给出一个合理的访问子树的顺序,以最小化(f[i])的值
我们先考虑最后访问的一棵子树,记(sum = sum siz[to])
那么最后一棵子树的贡献

[f[to] + sum - siz[to] ]

显然按(f[to] - siz[to])排序,最小的放最后,次小的放倒数第二,以此类推
用扰动法可以证明是对的

复杂度(O(nlogn))

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
using namespace std;
const int maxn = 500005,maxm = 100005,INF = 1000000000;
inline int read(){
	int out = 0,flag = 1; char c = getchar();
	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
	return out * flag;
}
int h[maxn],ne = 1;
struct EDGE{int to,nxt;}ed[maxn << 1];
inline void build(int u,int v){
	ed[++ne] = (EDGE){v,h[u]}; h[u] = ne;
	ed[++ne] = (EDGE){u,h[v]}; h[v] = ne;
}
int n,C[maxn],siz[maxn],f[maxn],fa[maxn],c[maxn],ci;
inline bool cmp(const int& a,const int& b){
	return f[a] - siz[a] < f[b] - siz[b];
}
void dfs(int u){
	siz[u] = 2;
	Redge(u) if ((to = ed[k].to) != fa[u]){
		fa[to] = u; dfs(to); siz[u] += siz[to];
	}
	ci = 0;
	Redge(u) if ((to = ed[k].to) != fa[u]) c[++ci] = to;
	sort(c + 1,c + 1 + ci,cmp);
	int sum = siz[u] - 2;
	REP(i,ci) f[u] = max(f[u],f[c[i]] - siz[c[i]] + sum),sum -= siz[c[i]];
	if (u != 1) f[u] = max(f[u] + 1,C[u] + 1);
}
int main(){
	n = read();
	REP(i,n) C[i] = read();
	for (int i = 1; i < n; i++) build(read(),read());
	dfs(1);
	printf("%d
",max(f[1],(n - 1) * 2 + C[1]));
	return 0;
}

原文地址:https://www.cnblogs.com/Mychael/p/9243487.html