题意描述:有一个编号为1-n的树,每个节点v都有一个区间[lv,rv],这个节点可以取这个范围内的值(记为av),两个直接相连的节点(u,v)产生的贡献为|au-av|,求整棵树的贡献和的最大值。
设f(x)=|x-a1|+|x-a2|+……+|x-ak|为节点x对答案产生的贡献,其中a1~ak为x的每个相连的节点的取值,显然f(x)max=max(f(lx),f(rx) ).
据此有如下贪心:为了使贡献和最大,每个节点必取最左或最右。
问题转化为:有一个编号为1-n的树,每个节点v的取值必为lv或rv(取值记为av),两个直接相连的节点(u,v)产生的贡献为|au-av|,求整棵树的贡献和的最大值。
取根节点为1,记f[i][0/1]为第i号节点取li或ri时,它的 子树 的贡献和的最大值,得状态转移方程:
f[x][0]+=max(f[y][0]+abs(v[x][0]-v[y][0]),f[y][1]+abs(v[x][0]-v[y][1]) );
f[x][1]+=max(f[y][0]+abs(v[x][1]-v[y][0]),f[y][1]+abs(v[x][1]-v[y][1]) );
其中li=v[i][0],ri=v[i][1].(i=1,2,……,n)
最后答案为max(f[1][0],f[1][1]).
时间复杂度O(n)
#include<bits/stdc++.h> #define ll long long using namespace std; ll head[200010],nxt[200010],to[200010],tot; ll f[100010][2]; ll v[100010][2]; ll vis[100010]; ll n; inline ll maxmy(ll a,ll b) { return a>b?a:b; } inline ll absmy(ll a) { return a<0?-a:a; } void add(ll x,ll y) { to[++tot]=y;nxt[tot]=head[x];head[x]=tot; return ; } void del() { tot=0; for(ll i=1;i<=n;++i) { f[i][0]=f[i][1]=head[i]=v[i][0]=v[i][1]=0; } return ; } void dfs(ll x) { ll y; for(ll ct=head[x];ct;ct=nxt[ct]) { y=to[ct]; if(!vis[y]) { vis[y]=1; dfs(y); vis[y]=0; f[x][0]+=maxmy(f[y][0]+absmy(v[x][0]-v[y][0]),f[y][1]+absmy(v[x][0]-v[y][1]) ); f[x][1]+=maxmy(f[y][0]+absmy(v[x][1]-v[y][0]),f[y][1]+absmy(v[x][1]-v[y][1]) ); } } } int main() { ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); ll t,x,y; cin>>t; while(t--) { del(); cin>>n; for(ll i=1;i<=n;++i) { cin>>v[i][0]>>v[i][1]; } for(ll i=1;i<n;++i) { cin>>x>>y; add(x,y);add(y,x); } vis[1]=1; dfs(1); vis[1]=0; cout<<maxmy(f[1][0],f[1][1])<<endl; } return 0; }