【描述】
有 N 城 市在一个国家,有一个且只有一个简单的路径每一对城市之间。 一个商人选择了一些路径和想赚尽可能多的钱在每个路径。 当他沿着一条路径,可以选择一个城市购买一些商品和出售他们在一个城市。 货物在所有的城市都是一样的,但价格是不同的。 现在你的任务是计算每条路径的最大利润。
【解】
这是一道树上的题目,找路径自然不必多说lca找到公共祖先路径也就找到了。但是这个题有个细节,就是要先买入后卖出,所以不能返回,也就是说价格低的要比价格高的在前面经过。最先想到的是dfs找答案,但很明显会TLE,因为Q和N太大了。所以就要想到优化了,得到公共祖先之后答案存在的范围有三种,第一种:答案在起点到公共祖先之间(最大值和最小值都在一边),第二种:答案在公共祖先和重点之间(同前),第三种:答案在两者之前(最小值在起点到公共祖先之间,最大值在公共最先到终点之间)。
一开始是想的在dfs上进行优化,设vis[i]标记i是否被询问过,rt[i]最近的关于点i的询问中i的祖先,minp[i]为i到rt[i]之间的最小价格,maxp[i]为i到rt[i]之间的最大价格,up[i]为从i到rt[i]之间的可赚取的最大收益,down[i]为从rt[i]到i之间的最大收益。然后按照公共祖先的顺序从大到小排列,边搜索边更新数组这样就相当于记忆化搜索,可以减少搜索时间。但我们发现,这些其实是可以通过倍增思想求得的。也就是说可以省掉rt数组和vis数组,minp[i][j]代表i向上跳j步的最小价格,maxp[i][j]代表i向上跳j步的最大价格,up[i][j]为i向上跳j步可赚取的最大收益,down[i][j]为公共祖先向下跳j步的最大收益。可以像如下更新数组
int ask_ans(int u,int p,int v)
{
int upmax=0,downmax=0,minu=1<<30,maxv=0;
for(int i=21;i>=0;i--)
if(dep[u]-(1<<i)>=dep[p])
{
upmax=max(maxp[u][i]-minu,max(up[u][i],upmax));//特别注意和下一句的顺序不能颠倒,取这段的最大值up[u][i]和这段和上一段的最大值maxp[u][i]-minu和现在得到的最大值对比
minu=min(minu,minp[u][i]);
u=f[u][i];
}
for(int i=21;i>=0;i--)
if(dep[v]-(1<<i)>=dep[p])
{
downmax=max(maxv-minp[v][i],max(down[v][i],downmax));
maxv=max(maxv,maxp[v][i]);
v=f[v][i];
}
return max(0,max((maxv-minu),max(upmax,downmax)));
}