Description
- 给定一颗树,每个点可以收获体力值a[i]且最多收获一次。每条边需要消耗体力值w[i],且每次经过都会消耗。等待1个单位时间回复1点体力值。
- 初始体力为0,求从1出发经过所有点并回到1,最小等待的时间。
n<=1e5
Solution
- 很容易想到DP,设f[i]表示i的子树遍历完后需要等待的最小时间,那么剩余的体力res[i]=sum[i]+f[i]-cost[i],sum表示点权和,cost表示边权和*2
- 那么影响到转移的实际上是儿子选取的顺序。
- 跟顺序有关,并且转移又比较朴素,显然是可以贪心的。
- 对于一个点x的所有儿子,如果以f[i]或res[i]排序显然是有问题的(我比赛的时候就打了这种显然错误的贪心。。。,后面的就没有想到了)。
- 因为我们没有考虑到从x下去的这条边对答案的影响。
- 所以我们改一下状态,将f[i]考虑上到i父亲那条边的边权的影响。
- 现在我们再重新考虑这个问题的模型:由于如果要休息,在根节点休息是不劣的。那么如果要选择一个儿子y,那么首先至少要休息到f[y],并且体力会加上(res[y]-f[y])(设为g[y])
- 我们现在要确定一个选择儿子的顺序使得刚开始休息的时间最少。
- 对于g[y]>=0的,说明原来是f[y],进去之后就变多了,显然我们可以按照f从小到大选取保证每一次的要补充的体力最少。
- 对于g[y]<0的,我们倒着选,假定最后剩下了s,那么反过来,s+|g[y]|>=f[y]才可以选择y。
- 即s>=f[y]-|g[y]|=f[y]+g[y],就可以选择y,并且s可以加|g[y]|。这与g[y]>=0的一模一样!
- 所以对于f[y]+g[y]从大到小选择就是原本的顺序啦!
- 思路巧妙(其实也可说很水)
- 贪心太难啦~
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define ll long long
#define maxn 100005
using namespace std;
int n,i,j,k,x,y,z;
int em,e[maxn*2],nx[maxn*2],ls[maxn];
ll a[maxn],ec[maxn*2],f[maxn],res[maxn],ecf[maxn];
int tot0,d0[maxn],tot1,d1[maxn];
int cmp1(int x,int y){return f[x]<f[y];}
int cmp2(int x,int y){return f[x]+res[x]>f[y]+res[y];}
void insert(int x,int y,int z){
em++; e[em]=y; nx[em]=ls[x]; ls[x]=em; ec[em]=z;
em++; e[em]=x; nx[em]=ls[y]; ls[y]=em; ec[em]=z;
}
void DFS(int x,int p){
for(int i=ls[x];i;i=nx[i]) if (e[i]!=p)
ecf[e[i]]=ec[i],DFS(e[i],x);
f[x]=ecf[x];
tot0=tot1=0;
for(int i=ls[x];i;i=nx[i]) if (e[i]!=p){
if (res[e[i]]>=0) d0[++tot0]=e[i];
else d1[++tot1]=e[i];
}
sort(d0+1,d0+1+tot0,cmp1);
sort(d1+1,d1+1+tot1,cmp2);
ll s=a[x];
for(int i=1;i<=tot0;i++) {
if (s>=f[d0[i]]) s+=res[d0[i]];
else f[x]+=f[d0[i]]-s,s=f[d0[i]]+res[d0[i]];
}
for(int i=1;i<=tot1;i++) {
if (s>=f[d1[i]]) s+=res[d1[i]];
else f[x]+=f[d1[i]]-s,s=f[d1[i]]+res[d1[i]];
}
if (s>=ecf[x]) s-=ecf[x]; else f[x]+=ecf[x]-s,s=0;
res[x]=s-f[x];
}
int main(){
freopen("horse.in","r",stdin);
freopen("horse.out","w",stdout);
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%lld",&a[i]);
for(i=1;i<n;i++){
scanf("%d%d%d",&x,&y,&z);
insert(x,y,z);
}
DFS(1,0);
printf("%lld
",f[1]);
}