Solution
挺有意思的一道树形DP,我理解了3个小时……
这道题目我们想一想后会发现大致要记录两个值:从i开始走再回到i的最大价值,从i开始不回到i的最大价值。 我们用方法一表示走某颗子树走下去再走回来,方法二表示走下去不回来。
我们用
f[i]
表示从i开始往子树走,再走回来的最大价值,(可以走很多棵子树),
Max[i]
表示走每棵子树用方法二和方法一的价值之差,显然方法二更优秀,因为不用走回来,所以Max是正的,也就是说最后的答案是
f[root]+Max[root]
。
这样定义其实是为了求Ans,我们有且仅有一次不回来的机会,所以要用在方法二最能体现价值的地方,也就是说Max的定义是一个差值,两种方法的差值,所以我们选Max最大的那个子树走方法二,即不会来,这样就是最优解。(做完题目要理解这句话的含义,Max就是为了平衡f多减的值)
dis[u][v]=d
f[u]=w[u]+∑v为u儿子max(f[u]−2∗d,0)
这个式子表示u往下走,假如再走回来的价值>0,当然是走,加上,不然就不走。
Max[u]=Max[v]−d+f[v](v走过方法一)
Max[u]=Max[v]−d+f[v]−(f[v]−2∗d)(v,没有走方法一)
走下去再走回来,假如没走过,代价是走一遍d,再加上走一些方法一和一条方法二的价值,这就是Max的定义,假如v走过方法一,那么把加上的减去即可,也就是- (f[v]−2∗d)
这两个合起来就是
Max[u]=Max[v]−d+f[v]−max(f[v]−2∗d,0)
要记住这个式子的含义,是一条d加上v的子树的价值
综上,我们得了50分
然后我们想如何换根,即O(1)转移
假设我们知道了u的f,Max,如何求v的?还是要平衡
这时的f,Max数组已经改变了含义,已经包括了了u上方的路径,即以u为根的f,Max
那么f[v]怎么求?它还差上方的u开始的这条路,因为f[u]可能包含从v向下走的路,我们要去掉,即 tmp=f[u]−2∗d−max(0,f[v]−2∗d) ,表示u即u上方的走去再回来的路径加上u、v连线的两倍,这时f[v]可以加上tmp,若tmp>0,否则不加
Max[v]怎么求?我们发现需要u不走v的Max,所以若Max[u]用到的是v,那么要用u的次大值,所以要在记录一个Max1数组表示次大值,只需求Max的时候同时更新即可,假设Max[u]不是用v来更新的,而且tmp>0那么平衡上面的需要Max[u]+d,+d是因为前面-d*2,不走回来只需要-d,所以+d来平衡,若tmp<0,那么上面的路没走,要用tmp+Max[u]+d,也是走过去再走回来的价值,跟上面比多了tmp?为什么,因为在上面的f[v]里加了tmp,这里没加,这样问题就解决了。
Problem
1678: 宝藏
时间限制: 1 Sec 内存限制: 256 MB
提交: 55 解决: 39
[提交][状态][讨论版]
题目描述
输入
输出
样例输入
6
1 2 1
2 3 3
3 4 36
3 6 13
3 5 2
6 8 9 10 13 1
样例输出
30
29
28
10
30
16
提示
CODE
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=300010,M=600010;
ll f[N],Max[N],Max1[N],w[N],c[N],Head[N],n,tot;
struct Edge{
ll v,d,next;
}edge[M];
void add(ll x,ll y,ll z)
{
edge[++tot]=(Edge){y,z,Head[x]};
Head[x]=tot;
}
void calc(ll u,ll x,ll v)
{
if (x>Max[u])
{
Max1[u]=Max[u];
Max[u]=x;
c[u]=v;
}
else Max1[u]=max(Max1[u],x);
}
void dfs1(ll u,ll pre)
{
f[u]=w[u];
for (ll i=Head[u];i;i=edge[i].next)
{
ll v=edge[i].v,d=edge[i].d;
if (v==pre) continue;
dfs1(v,u);
f[u]+=max(f[v]-2*d,0LL);
calc(u,Max[v]-d+f[v]-max(0LL,f[v]-2*d),v);
}
}
void dfs2(ll u,ll pre)
{
for (ll i=Head[u];i;i=edge[i].next)
{
ll v=edge[i].v,d=edge[i].d;
if (v==pre) continue;
ll tmp=f[u]-2*d-max(0LL,f[v]-2*d);
if (tmp>0)
{
f[v]+=tmp;
if (c[u]==v) calc(v,Max1[u]+d,u);
else calc(v,Max[u]+d,u);
}
else
{
//f[v]+=tmp;
if (c[u]==v) calc(v,tmp+Max1[u]+d,u);
else calc(v,tmp+Max[u]+d,u);
}
//f[v]+=max(0,f[u]-2*d-max(0,f[v]-2*d));
//calc(v,c[u]==v?Max1[u]+d:Max[u]+d,u);
dfs2(v,u);
}
}
int main()
{
scanf("%lld",&n);
for (ll i=1;i<n;i++)
{
ll x,y,z;
scanf("%lld%lld%lld",&x,&y,&z);
add(x,y,z); add(y,x,z);
}
for (ll i=1;i<=n;i++) scanf("%lld",&w[i]);
dfs1(1,0);
dfs2(1,0);
for (ll i=1;i<=n;i++)
printf("%lld\n",f[i]+Max[i]);
return 0;
}
下面是精简版
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=300010,M=600010;
ll f[N],Max[N],Max1[N],w[N],c[N],Head[N],n,tot;
struct Edge{
ll v,d,next;
}edge[M];
void add(ll x,ll y,ll z)
{
edge[++tot]=(Edge){y,z,Head[x]};
Head[x]=tot;
}
void calc(ll u,ll x,ll v)
{
if (x>Max[u])
{
Max1[u]=Max[u];
Max[u]=x;
c[u]=v;
}
else Max1[u]=max(Max1[u],x);
}
void dfs1(ll u,ll pre)
{
f[u]=w[u];
for (ll i=Head[u];i;i=edge[i].next)
{
ll v=edge[i].v,d=edge[i].d;
if (v==pre) continue;
dfs1(v,u);
f[u]+=max(f[v]-2*d,0LL);
calc(u,Max[v]-d+f[v]-max(0LL,f[v]-2*d),v);
}
}
void dfs2(ll u,ll pre)
{
for (ll i=Head[u];i;i=edge[i].next)
{
ll v=edge[i].v,d=edge[i].d;
if (v==pre) continue;
ll tmp=f[u]-2*d-max(0LL,f[v]-2*d);
f[v]+=max(0LL,tmp);
calc(v,((c[u]==v)?Max1[u]:Max[u])+min(0LL,tmp)+d,u);
dfs2(v,u);
}
}
int main()
{
scanf("%lld",&n);
for (ll i=1;i<n;i++)
{
ll x,y,z;
scanf("%lld%lld%lld",&x,&y,&z);
add(x,y,z); add(y,x,z);
}
for (ll i=1;i<=n;i++) scanf("%lld",&w[i]);
dfs1(1,0);
dfs2(1,0);
for (ll i=1;i<=n;i++)
printf("%lld\n",f[i]+Max[i]);
return 0;
}