FarmCraft
# Description
mhy住在一棵有n个点的树的1号结点上,每个结点上都有一个妹子。
mhy从自己家出发,去给每一个妹子都送一台电脑,每个妹子拿到电脑后就会开始安装zhx牌杀毒软件,第i个妹子安装时间为Ci。
树上的每条边mhy能且仅能走两次,每次耗费1单位时间。
mhy送完所有电脑后会回自己家里然后开始装zhx牌杀毒软件。
卸货和装电脑是不需要时间的。
求所有妹子和mhy都装好zhx牌杀毒软件的最短时间。
# Format
## Input
第一行两个正整数N
第二行N个数字,代表ci
下面N-1行,每行2个正整数a,b用来描述这个树
2<=N<=5 00 000
1<=Ci<=10^9
## Output
共M行,如题所述.
# Samples
```input1
6
1 8 9 6 3 2
1 3
2 3
3 4
4 5
4 6
```
```output1
11
```
Hint
按这个方式行进
3, 2, 4, 5, 6, 1
于是过了11, 10, 10, 10, 8, 9分钟后,电脑安装完毕
贪心
设f[i]表示子树i全部安装完成所需的最小总时间。
那么对于一个某结点x,f[x]一定大于等于c[x]。
若其为非叶子结点,考虑其子树a和b。
若先安装a再安装b,则a消耗的时间为f[a]+1,这个1是从x走到a所花的时间
从x出发走完a这个子树,再回到x,一共走过了si[a]+1个点,共si[a]+1-1条边,即si[a]条边
共花时间2*si[a],然后再发一个时间走到b.
例如下图

第一次走到3号点的时间为1,第1次走到2号点的时间为7
b消耗的时间为2*si[a]+f[b]+1,
若先安装b再安装a,则a消耗的时间为2*si[b]+f[a]+1,b消耗的时间为f[b]+1
若先安装a合适,则必有2*si[a]+f[b]+1<2*si[b]+f[a]+1,即2*si[a]-f[a]<f2*si[b]-f[b]
于是可以将x的所有子树按照f-2si从小到大排序,依次统计答案。
由于题目描述必须先完成2~n再完成1,所以应先将1的时间看作0,再分开计算。
#include <cstdio>
#include <algorithm>
#define N 500010
using namespace std;
struct data {
int f , si;
} k[N] , a[N];
int head[N] , to[N << 1] , nextt[N << 1] , cnt , c[N];
bool cmp(data a , data b)
{
//return a.f - 2 * a.si > b.f - 2 * b.si;
//return 2*si[a]+f[b]+1 < 2*si[b]+f[a]+1
return 2*a.si-a.f<2*b.si-b.f;
}
void add(int x , int y) {
to[++cnt] = y , nextt[cnt] = head[x] , head[x] = cnt;
}
void dfs(int x , int fa)
{
int i , tot = 0 , now = 1;
k[x].f = c[x] ;
k[x].si = 1;
for(i = head[x] ; i ; i = nextt[i])
if(to[i] != fa) dfs(to[i] , x) , k[x].si += k[to[i]].si;
for(i = head[x] ; i ; i = nextt[i])
if(to[i] != fa) a[++tot] = k[to[i]];
sort(a + 1 , a + tot + 1 , cmp);
for(i = 1 ; i <= tot ; i ++ )
k[x].f = max(k[x].f , a[i].f + now) , now += 2 * a[i].si;
}
int main() {
int n , i , x , y , t;
scanf("%d" , &n);
for(i = 1 ; i <= n ; i ++ )
scanf("%d" , &c[i]);
t = c[1] , c[1] = 0;
for(i = 1 ; i < n ; i ++ )
scanf("%d%d" , &x , &y) , add(x , y) , add(y , x);
dfs(1 , 0);
printf("%d\n" , max(k[1].f , t + 2 * (k[1].si - 1)));
return 0;
}

浙公网安备 33010602011771号