这题是一个在一开始就能吓住人,然后分析分析,再分析分析,再分析分析,发现能做的好题。
题意:给出一个结点带权price的树状图(结点数<=50000),询问(询问次数<=50000)树中从u到v中能赚到的最多的差价是多少。
首先最朴素的想法是,找出u到v的路径(用到LCA),完整地访问一遍得结果。
朴素的想法是出发点,虽不可行,觉得有可行的优化空间。
优化线索:询问之间有路径的重复,重复的部分可通过一些记录来除掉重复访问和计算;
对于这条线索,我的想法是可以先处理路径较短的询问,再处理路径较长的询问,这样能最好的记录有效的实时信息。
很抽象?先随便确定一个根把树变成有根树之后,对树按层次遍历来编号num[i],那么对于所有的询问(u,v),按其LCA(u,v)的num从大到小排序,于是就可以按路径的长短顺序来处理各个询问了。然后一边处理询问一边记录相关价格信息。
这里我记录的有: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之间的最大收益。
在询问(u,v)中,求得这些信息时,按朴素的想法从u向上到LCA(u,v),再从v到LCA(u,v)遍历一遍,遍历时遇到询问过的点i,根据那些minp,maxp,up,down什么的,特殊处理一下后可直接跳到rt[i]的父节点去,那么所有的点访问次数就降下很多了。
得到那些信息后,询问的答案也就出来了,有ans=max{up[u],down[v],maxp[v]-minp[u]},注意LCA(u,v)==u或者LCA(u,v)==v的情况。
//还有不明白的见代码
整个的分析过程花去了太多时间,还好是1Y。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <queue>
#include <algorithm>
#define Root 1
using namespace std;
const int NN=150000;
const int INF=0x3fffffff;
int en,head[NN];
struct Edge
{
int v,next;
} e[NN];
void add(int u,int v)
{
e[en].v=v;
e[en].next=head[u];
head[u]=en++;
e[en].v=u;
e[en].next=head[v];
head[v]=en++;
}
int fa[NN],num[NN];
bool vis[NN];
void bfs()
{
int u,v,i,cnt;
memset(vis,0,sizeof(vis));
num[Root]=vis[Root]=cnt=1;
fa[Root]=-1;
queue<int> q;
q.push(Root);
while (!q.empty())
{
u=q.front();
q.pop();
for (i=head[u]; i!=-1; i=e[i].next)
{
v=e[i].v;
if (vis[v]) continue;
vis[v]=true;
num[v]=++cnt;
fa[v]=u;
q.push(v);
}
}
}
int bn,depth,b[NN],f[NN],at[NN];
void dfs(int u)
{
int tmp=++depth;
b[++bn]=tmp; f[tmp]=u; at[u]=bn;
for (int i=head[u]; i!=-1; i=e[i].next)
{
int v=e[i].v;
if (fa[u]==v) continue;
dfs(v);
b[++bn]=tmp;
}
}
int dp[NN][30];
void LCA_init()
{
bn=depth=0;
dfs(Root);
for (int i=1; i<=bn; i++) dp[i][0]=b[i];
int m=floor(log(bn*1.0)/log(2.0));
for (int j=1; j<=m; j++)
for (int i=1; i<=bn-(1<<j)+1; i++)
dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
int rmq(int l,int r)
{
int k=floor(log((r-l+1)*1.0)/log(2.0));
return min(dp[l][k],dp[r-(1<<k)+1][k]);
}
int LCA(int a,int b)
{
if (at[a]>at[b]) swap(a,b);
int k=rmq(at[a],at[b]);
return f[k];
}
int price[NN],ans[NN],rt[NN],minp[NN],maxp[NN],up[NN],down[NN];
void get(int v,int x)
{
if (vis[v] && rt[v]==x) return;
int u=v;
int a1,a2,a3,b1,b2,b3,c1,c2;//a1b1c1求up用,a2b2c2求down用,a3b3求minp和maxp用
a1=a2=a3=INF; b1=b2=b3=0; c1=c2=0;
while (u!=fa[x])
{
if (vis[u])
{
if (up[u]>c1) c1=up[u];
if (down[u]>c2) c2=down[u];
if (maxp[u]-a1>c1) c1=maxp[u]-a1;
if (minp[u]<a1) a1=b1=minp[u];
if (b2-minp[u]>c2) c2=b2-minp[u];
if (maxp[u]>b2) a2=b2=maxp[u];
if (minp[u]<a3) a3=minp[u];
if (maxp[u]>b3) b3=maxp[u];
u=fa[rt[u]];
}
else
{
if (price[u]<a1) a1=b1=price[u];
if (price[u]>b1) b1=price[u];
if (price[u]>b2) a2=b2=price[u];
if (price[u]<a2) a2=price[u];
if (price[u]<a3) a3=price[u];
if (price[u]>b3) b3=price[u];
u=fa[u];
}
if (b1-a1>c1) c1=b1-a1;
if (b2-a2>c2) c2=b2-a2;
}
minp[v]=a3; maxp[v]=b3;
up[v]=c1; down[v]=c2;
rt[v]=x;
vis[v]=true;
}
struct question
{
int u,v,x,id;
bool operator <(const question a)const
{
return num[x]>num[a.x];
}
} que[NN];
int main()
{
int i,u,v,n,q,x;
scanf("%d",&n);
en=0;
for (i=1; i<=n; i++)
{
scanf("%d",&price[i]);
head[i]=-1;
}
for (i=1; i<n; i++)
{
scanf("%d%d",&u,&v);
add(u,v);
}
bfs();
LCA_init();
scanf("%d",&q);
for (i=1; i<=q; i++)
{
scanf("%d%d",&u,&v);
que[i].u=u;
que[i].v=v;
que[i].x=LCA(u,v);
que[i].id=i;
}
sort(que+1,que+1+q);
memset(vis,0,sizeof(vis));
for (i=1; i<=q; i++)
{
u=que[i].u;
v=que[i].v;
x=que[i].x;
get(u,x);
get(v,x);
if (u==x) ans[que[i].id]=down[v];
else if (v==x) ans[que[i].id]=up[u];
else ans[que[i].id]=max(max(up[u],down[v]),maxp[v]-minp[u]);
}
for (i=1; i<=q; i++) printf("%d\n",ans[i]);
return 0;
}