传送门
preface
下午颓的时候看到这道题,当时也没怎么想,就去找题解,结果听不懂ta在讲啥。晚上自己回来弄一弄就完了,就写个题解翻译一下。
分析
这个树形dp就没什么好说的了。用dp[i]表示把以i为根的子树全处理完后最少还要多少时间(划重点)。刚开始想最常规的思路,提交一发只对了一个点,然后才改用题解的dp。这样的话,最后只用判断c1和dp[1]哪个大再加上所有边的两倍就可以了。
考虑转移。贪心的想,我们每到一个节点就开始安装,然后走这个节点的儿子里dp值最大的那个,显然,这样走可以保证不比其他方式劣。所以把i的儿子用一个vector全部记录下来,按照dp值从大到小排序。因为dp[i]受i的儿子中最大dp值限制,所以dp[i]就是每个儿子的dp值减去按照贪心策略遍历每个儿子的时间的最大值。再把处理后的dp[i]和ci-(sizi-1)*2取max就可以了。
p.s.这题也有省选难度?
code
#include<bits/stdc++.h> #define ll long long #define maxn 500010 #define reg register using namespace std; int n,w[maxn],head[maxn],k,dp[maxn],dep[maxn],siz[maxn]; struct node { int to,nxt; } edge[maxn*2]; void add(int u,int v) { edge[++k].nxt=head[u]; edge[k].to=v; head[u]=k; } bool cmp(int x,int y) { return dp[x]>dp[y]; } void dfs(int u,int fa) { siz[u]=1; vector<int> tmp; for(int i=head[u]; i; i=edge[i].nxt) { int v=edge[i].to; if(v!=fa) { tmp.push_back(v); dep[v]=dep[u]+1; dfs(v,u); siz[u]+=siz[v]; } } if(u!=1) dp[u]=w[u]-(siz[u]-1)*2; int sum=(siz[u]-1)*2; sort(tmp.begin(),tmp.end(),cmp); for(int i=0,sz=tmp.size(); i<sz; i++) { sum-=siz[tmp[i]]*2; dp[u]=max(dp[u],dp[tmp[i]]-sum-1); } dp[u]=max(dp[u],0); } int main() { scanf("%d",&n); for(int i=1; i<=n; i++) scanf("%d",&w[i]); int x,y; for(int i=1; i<n; i++) { scanf("%d%d",&x,&y); add(x,y),add(y,x); } dfs(1,1); printf("%d\n",max(dp[1],w[1])+(n-1)*2); return 0; }