题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6662
题目大意:一棵树,点权是一个差值a-b,两个人(张和刘)在树上旅游,每个点只能走过一次,张想要走过的节点和最大,刘要走过的和最小,张先手然后两人交替选择下一个相邻的点走直到无路可走停止,两个人都会选择对自己的最优策略。求最大的和。
ps:题目原意是张在每一个节点有一个满意度,刘有一个满意度,求满意度的差别最大。然后我理解成了两个人都想要差别的绝对值最大qwq,比赛时理解错题写错了
题解:树形dp
每段旅程的终点为叶子结点
固定好树后,以每个节点为起点,有两种走法,向上和向下 我们要选择其中最优的
1. 向下维护:一遍dfs
maxx[v] : v点张选择往下走得最大值
minn[v] : v点刘选择往下走得最小值
maxx[v]= max( minn[ son ] ) + v[i] 张会选择往后 刘做选择 得到的最大的
minn[v]= min( maxn[ son ] ) + v[i] 刘会选择往后 张做选择 得到的最小的
2.向上维护:要考虑当前点的后手(父亲)会做对他最优的选择
当前点父亲 可以是沿着父亲一直往上走,或者向下走到兄弟的最优
如果当前点是父亲往下走最优时的那条路,就是往上走或者走父亲向下的 次优
zhang[v] : v点张选择往上走得最大值
liu[v] : v点刘选择往上走得最小值
zhang[v] = min( liu [ father ] , minn[ father ] ) 当前张做选择往上走 父亲刘会选择 向上 或 向下(1中维护过) 中更小的一个走
liu[v] = max( zhang [ father ], maxx[ father ] ) 当前刘做选择往上走 父亲张会选择 向上 或 向下 中更大的一个走
用每个点liu做选择的最优答案(说明这个起始点是张选的)更新答案
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int const maxn=1e5+100;
int tot,n,head[maxn],fa[maxn];
long long ans,son[maxn],a[maxn],minn[maxn],maxx[maxn],fmi[maxn],fmaa[maxn],danma[maxn],danmi[maxn];
ll zhang[maxn],liu[maxn];
//max[x]当前x点往下走张做选择 min[x] 当前点x往下走刘做选择
//zhang[x]当前点往上走张做选择 liu[x] 往上走刘做选择
struct edge{
ll to, nex;
}e[maxn<<1];
void builde(int x,int y){
e[++tot].to=y,e[tot].nex=head[x];head[x]=tot;
}
inline int get_num(){
char ch;
int num=0;
ch=getchar();
while(ch<'0'||ch>'9'){ch=getchar();}
while(ch>='0'&&ch<='9'){num=(num<<3)+(num<<1)+ch-'0';ch=getchar();}
return num;
}
void dfs(int x){
ll nmin=2e18,nmax=-(2e18),nit=2e18,nat=-(2e18);//记录前两大和前两小
int to;
minn[x]=maxx[x]=danmi[x]=danma[x]=a[x];
for(int i=head[x];i;i=e[i].nex){
to=e[i].to;
if(to==fa[x])continue;
fa[to]=x;
dfs(to);
son[x]++;
if(maxx[to]<nmin){ nit=nmin, nmin=maxx[to],fmi[x]=to;}
else if(maxx[to]<nit)nit=maxx[to];//刘会选择后手张做选择中最小的
if(minn[to]>nmax){ nat=nmax, nmax=minn[to],fmaa[x]=to;}
else if(minn[to]>nat)nat=minn[to];//张会选择后手刘做选择中最大的
}
if(son[x]){
maxx[x]+=nmax,minn[x]+=nmin;
}
if(son[x]>1){
danmi[x]=nit+a[x];danma[x]=nat+a[x];
}
}
void dfs2(int x){
for(int i=head[x];i;i=e[i].nex){
int to=e[i].to;
if(to==fa[x])continue;
if(son[x]==1){
zhang[to]=liu[x]+a[to];//如果儿子只有一个,那这个儿子只能沿着父亲往上走
liu[to]=zhang[x]+a[to];
}
else{//如果有多个儿子,它可以沿着父亲往上走,或者走他的兄弟中对他最优的,【注意如果是原先父亲结点的最优来源,那应该走次优的那条兄弟路线
ll rr=(to==fmaa[x])?rr=danma[x]:maxx[x];
ll lu=(to==fmi[x])?lu=danmi[x]:minn[x];
if(x!=1)rr=max(zhang[x],rr),lu=min(liu[x],lu);//
liu[to]=rr+a[to];
zhang[to]=lu+a[to];
}
dfs2(to);
}
}
int main(){
int t;
scanf("%d",&t);
while(t--){
memset(head,0,sizeof head);
memset(son,0,sizeof(son));
memset(fmi,0,sizeof fmi);
memset(fmaa,0,sizeof fmaa);
scanf("%d",&n);
tot=0;
for(int i=1;i<=n;i++)a[i]=get_num();
for(int i=1;i<=n;i++)a[i]-=get_num();
int p,q;
for(int i=1;i<n;i++){
p=get_num(),q=get_num();
builde(p,q);builde(q,p);
}
dfs(1);
zhang[1]=liu[1]=a[1];ans=minn[1];
dfs2(1);
//每个点只能用liu(起始点刘做选择说明起始点是张选的)更新一次答案,用上下走或向上走最优的那个
for(int i=2;i<=n;i++){
if(son[i])ans=max(ans,min(liu[i],minn[i]));//非儿子可以选择往上或往下走对自己最优的
else ans=max(ans,liu[i]);//儿子做起点只能往上走
}
//for(int i=1;i<=n;i++)cout<<maxx[i]<<' '<<minn[i]<<endl;
printf("%lld\n",ans);
}
return 0;
}
标程:
1 #include<bits/stdc++.h>
2 #define maxn 202000
3 #define x first
4 #define y second
5
6 using namespace std;
7 typedef long long ll;
8 typedef pair<ll,ll> pi;
9 const ll inf=1e16;
10 ll a[maxn],n,k,query,ans,p[maxn],q[maxn],d[maxn];
11 vector <int> h[maxn];
12 pi f[maxn],g[maxn];
13
14 void dfs(int fa,int u)
15 {
16 for (int i=0;i<h[u].size();i++)
17 {
18 int v=h[u][i];
19 if (v==fa) continue;
20 dfs(u,v),d[u]++;
21 if (f[u].x<a[u]+g[v].x) f[u].y=f[u].x,f[u].x=a[u]+g[v].x;
22 else if (f[u].y<a[u]+g[v].x) f[u].y=a[u]+g[v].x;
23 if (g[u].x>a[u]+f[v].x) g[u].y=g[u].x,g[u].x=a[u]+f[v].x;//g先手
24 else if (g[u].y>a[u]+f[v].x) g[u].y=a[u]+f[v].x;
25 }
26 if (!d[u]) f[u].x=f[u].y=g[u].x=g[u].y=a[u];
27 }
28
29 void dfs2(int fa,int u)
30 {
31 for (int i=0;i<h[u].size();i++)
32 {
33 int v=h[u][i]; ll r;
34 if (v==fa) continue;
35 if (d[u]==1) p[v]=q[u]+a[v],q[v]=p[u]+a[v];
36 else {
37 r=(f[u].x==g[v].x+a[u])?f[u].y:f[u].x;
38 if (u!=1) r=max(r,q[u]); p[v]=r+a[v];
39 r=(g[u].x==f[v].x+a[u])?g[u].y:g[u].x;
40 if (u!=1) r=min(r,p[u]); q[v]=r+a[v];
41 }
42 dfs2(u,v);
43 }
44 }
45 int main()
46 {
47 //freopen("test1.in","r",stdin);
48 //freopen("test2.out","w",stdout);
49 scanf("%lld",&query);
50 while (query--){
51 scanf("%lld",&n);
52 //printf("%d\n", n);
53 for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
54 for (int i=1;i<=n;i++) {scanf("%lld",&k); a[i]-=k;}
55 for (int i=1;i<=n;i++) h[i].clear(),d[i]=0,f[i].x=f[i].y=-inf,g[i].x=g[i].y=inf;
56 for (int i=1;i<n;i++)
57 {
58 int u,v; scanf("%d%d",&u,&v);
59 h[u].push_back(v);
60 h[v].push_back(u);
61 }
62 dfs(0,1); p[1]=q[1]=a[1]; dfs2(0,1); ans=g[1].x;
63 //for (int i=1;i<=n;i++) cout << p[i] << ' ' << q[i] << endl;
64 //for (int i=1;i<=n;i++) cout << f[i].x << ' ' << f[i].y << ' ' << g[i].x << ' ' << g[i].y << endl;
65 for (int i=2;i<=n;i++) if (d[i]) ans=max(ans,min(g[i].x,p[i])); else ans=max(ans,p[i]);
66 cout << ans << endl;
67 }
68 return 0;
69 }