东非大裂谷
### Description
数据范围:\(1<=N<=10^5,1<=W_i<=10^9\)
Solution
感觉最近有点不在状态。。这题场上居然没做出来也是服了
(代爷:“这不是普及组题吗?”,然后7minA掉了qwq)
从满足题意的分组的性质入手:
(1)如果一条链是一组,那么这条链的两个端点的值一定分别对应最大值和最小值(否则把这个点从这条链里面去掉,然后并到另一组里面并不会使答案变劣)
(2)如果一条链是一组,那么这条链的点权值随深度增加单调递增或递减(如果不单调的话,我们可以从破坏单调性的地方断开变成多条链,并不会使答案变劣)
有了这两个性质之后dp就很显然了
我们对于每一个点维护该点为链头的链中递增或递减时子树内的答案最大值,\(f[x][0]\)表示递增的答案,\(f[x][1]\)表示递减的答案,记\(sum=\sum\limits_{u\in son(x)}max(f[u][0],f[u][1])\),那么有:
\[\begin{aligned}
&(u\in son(x)\&\&w[u]>w[x])\\
&f[x][0]=max(sum-max(f[u][0],f[u][1])+f[u][0]+w[u]-w[x])\\
\\
&(u\in son(x)\&\&w[u]<w[x])\\
&f[x][1]=max(sum-max(f[u][0],f[u][1])+f[u][1]+w[x]-w[u])
\end{aligned}
\]
最后\(ans=max(f[x][0],f[x][1],\sum\limits_{u\in son(1)}max(f[u][0],f[u][1]))\)
mark:(弱智操作)dp的时候不要想当然地忽略掉路径上其他分叉的贡献。。
mark:单调性单调性单调性qwq
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int N=1e5+10;
struct xxx{
int y,nxt;
}a[N*2];
int h[N],w[N];
ll f[N][2];
int n,m,tot;
ll ans;
void add(int x,int y){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;}
void dfs(int x){
int u;
ll sum=0;
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
dfs(u);
sum+=max(f[u][0],f[u][1]);
}
f[x][0]=f[x][1]=sum;
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (w[x]<=w[u])
f[x][0]=max(f[x][0],sum-max(f[u][0],f[u][1])+f[u][0]+w[u]-w[x]);
if (w[x]>=w[u])
f[x][1]=max(f[x][1],sum-max(f[u][0],f[u][1])+f[u][1]+w[x]-w[u]);
}
if (x==1){
ans=sum;
ans=max(ans,max(f[x][0],f[x][1]));
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int x,y;
scanf("%d",&n);
memset(h,-1,sizeof(h));
tot=0;
for (int i=1;i<=n;++i) scanf("%d",w+i);
for (int i=1;i<n;++i){
scanf("%d%d",&x,&y);
add(x,y);
}
ans=0;
dfs(1);
printf("%lld\n",ans);
}