【题目链接】 http://poj.org/problem?id=3728
【题目大意】
给出一棵树,每个点上都可以交易货物,现在给出某货物在不同点的价格,
问从u到v的路程中,只允许做一次买入和一次卖出,最多能得到多少钱。
【题解】
我们维护一个up表示,x与父节点的连线中,
最大值在靠近父节点的位置时最小值与最大值的最大差值
dw表示,x与父节点的连线中,最小值在靠近父节点的位置时最小值与最大值的最大差值
Min和Max分别表示x到父节点中的最大值和最小值
对于询问x到y的答案,我们发现以LCA为父节点时,答案只会是dwx,upy,和Maxx-Miny中的最优值,
所以我们在每个查询的LCA位置计算答案,自低向上维护四个数组,
这个用并查集就可以顺利完成了。
【代码】
#include <cstdio> #include <algorithm> #include <vector> #include <cstring> using namespace std; const int N=50010; int n,m,f[N],ans[N],vis[N],Max[N],Min[N],up[N],dw[N],val[N]; struct edge{int x,id;}; struct data{int x,y;}p[N]; vector<edge> Q[N]; vector<int> G[N],a[N]; int sf(int x){ if(f[x]==x)return x; int fx=f[x]; f[x]=sf(f[x]); up[x]=max(max(up[x],up[fx]),Max[fx]-Min[x]); dw[x]=max(max(dw[x],dw[fx]),Max[x]-Min[fx]); Max[x]=max(Max[x],Max[fx]); Min[x]=min(Min[x],Min[fx]); return f[x]; } void dfs(int u){ vis[u]=1; Max[u]=Min[u]=val[u]; up[u]=dw[u]=0; for(int i=0;i<Q[u].size();i++){ edge e=Q[u][i]; if(vis[e.x])a[sf(e.x)].push_back(e.id); } for(int i=0;i<G[u].size();i++){ int v=G[u][i]; if(!vis[v]){dfs(v);f[v]=u;} } for(int i=0;i<a[u].size();i++){ int id=a[u][i]; int x=p[id].x,y=p[id].y; sf(x); sf(y); ans[id]=max(max(up[x],dw[y]),Max[y]-Min[x]); ans[id]=max(0,ans[id]); } } void solve(){ for(int i=1;i<=n;i++)scanf("%d",&val[i]); for(int i=1;i<n;i++){ int u,v; scanf("%d%d",&u,&v); G[u].push_back(v); G[v].push_back(u); }for(int i=1;i<=n;i++)f[i]=i; scanf("%d",&m); for(int i=1;i<=m;i++){ int u,v; scanf("%d%d",&u,&v); Q[u].push_back(edge{v,i}); Q[v].push_back(edge{u,i}); p[i].x=u;p[i].y=v; }memset(vis,0,sizeof(vis)); dfs(1); for(int i=1;i<=m;i++)printf("%d ",ans[i]); } int main(){ while(~scanf("%d",&n))solve(); return 0; }