P3574
[POI2014]FAR-FarmCraft
我们可以先做树形 DP,虽然从复杂度上来讲是不对的,但从某种意义上讲,我们明白了这个答案和遍历子树的顺序有着很大的关系。
我们令
那么我们假设
这里的
然后我们知道,
这里加 2 是
那么如果说我们先遍历
假如说,我们先遍历
即:
又因为我们知道下面的条件:
那么可以大力分类讨论:
-
若
f_u<f_v+siz_u+2 ,则\max\{f_u,f_v+siz_u+2\}=f_v+siz_u+2 ,也就有f_v+siz_u+2<\max\{f_v,f_u+siz_v+2\} ;又因为f_v+siz_u+2>f_v ,那么只有可能f_v+siz_u+2<f_u+siz_v+2 ,即f_v-siz_v<f_u-siz_u 。 -
若
f_u\ge f_v+siz_u+2 ,又因为f_u+siz_v+2>f_u ,所以f_u+siz_v+2>f_u\ge f_v+siz_u+2 ,即f_u+siz_v+2>f_v+siz_u+2 ,即f_v-siz_v<f_u-siz_u 。
综上,满足
换句话说,这个遍历的顺序是可以贪心求解的。我们可以将
因为所有点都只经过一次排序,遍历的复杂度是线性的,所以说总的时间复杂度是
然后就是一些细节问题,我们针对每颗子树,子树的根节点一定是第一个被送到电脑的;然而节点 1(即整棵树的根节点)却不是,它是最后一个被送到的,因此要单独拿出来,与子树内部的点比较再得出答案。
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=5e5;
ll n,u,v,tot;
ll c[N+5],f[N+5],siz[N+5],tmp[N+5];
ll ver[N*2+5],nxt[N*2+5],head[N+5];
bool cmp(ll x,ll y) {
return f[x]-siz[x]>f[y]-siz[y];
}
void dfs(ll p,ll fath) {
if(p!=1) f[p]=c[p];
for(ll i=head[p];i;i=nxt[i]) {
if(ver[i]==fath) continue;
dfs(ver[i],p);
}
ll cnt=0;
for(ll i=head[p];i;i=nxt[i]) {
if(ver[i]==fath) continue;
tmp[++cnt]=ver[i];
}
sort(tmp+1,tmp+cnt+1,cmp);
for(ll i=1;i<=cnt;i++) {
f[p]=max(f[p],siz[p]+f[tmp[i]]+1);
siz[p]+=siz[tmp[i]]+2;
}
}
void add(ll u,ll v) {
ver[++tot]=v;nxt[tot]=head[u];head[u]=tot;
}
inline ll read() {
ll ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-f;ch=getchar();}
while(ch>='0'&&ch<='9') {ret=(ret<<3)+(ret<<1)+ch-'0';ch=getchar();}
return ret*f;
}
void write(ll x) {
static char buf[22];static ll len=-1;
if(x>=0) {
do{buf[++len]=x%10+48;x/=10;}while(x);
}
else {
putchar('-');
do{buf[++len]=-(x%10)+48;x/=10;}while(x);
}
while(len>=0) putchar(buf[len--]);
}
int main() {
n=read();
for(ll i=1;i<=n;i++) {
c[i]=read();
}
for(ll i=1;i<n;i++) {
u=read();v=read();
add(u,v);add(v,u);
}
dfs(1,0);
write(max(f[1],siz[1]+c[1]));
return 0;
}