解题思路
其实刚看完题还是能想到倍增的,但是因为没推出样例就没打。。啊这。。
题目有两个很坑的点,
- “重要度大的客户必须在重要度小的客户后面”,意思是重要度大的客户的深度必须比重要度小的客户的深度小,所以我们求的重要程度相差最大的两个客户,大的要比小的的深度小。
- 从X到根走k步中如果整段路上的点的重要度一直是递减的就是不合法的,输出0,若有任意一小段没有递减就是合法的。
OK,成功跳坑。
回到解题,我们设数组 f_{i,j} 表示第 i 个节点往上跳 2^j 次到达的父亲, minn_{i,j} 表示这段的最小值, maxn_{i,j} 表示这段的最大值, g_{i,j} 表示
这段的最大差值,也就是答案,q_{i,j} 表示这段是否合法。
易得:
f [ x ] [ i ] = f [ f [ x ] [ i − 1 ] ] [ i − 1 ] ; f[x][i]=f[f[x][i-1]][i-1]; f[x][i]=f[f[x][i−1]][i−1];
m a x n [ x ] [ i ] = m a x ( m a x n [ f [ x ] [ i − 1 ] ] [ i − 1 ] , m a x n [ x ] [ i − 1 ] ) ; maxn[x][i]=max(maxn[f[x][i-1]][i-1],maxn[x][i-1]); maxn[x][i]=max(maxn[f[x][i−1]][i−1],maxn[x][i−1]);
m i n n [ x ] [ i ] = m i n ( m i n n [ f [ x ] [ i − 1 ] ] [ i − 1 ] , m i n n [ x ] [ i − 1 ] ) ; minn[x][i]=min(minn[f[x][i-1]][i-1],minn[x][i-1]); minn[x][i]=min(minn[f[x][i−1]][i−1],minn[x][i−1]);
接着,对于q来说这段里有一小段为合法的,这段就合法,SO
i f ( q [ x ] [ i − 1 ] ∣ ∣ q [ f [ x ] [ i − 1 ] ] [ i − 1 ] ) if(q[x][i-1]||q[f[x][i-1]][i-1]) if(q[x][i−1]∣∣q[f[x][i−1]][i−1])
q [ x ] [ i ] = 1 ; q[x][i]=1; q[x][i]=1;
以上也算是在预处理,最后就像求
L
C
A
LCA
LCA一样,从x往上爬到
d
e
p
[
x
]
−
k
+
1
dep[x]-k+1
dep[x]−k+1处求这段路上的最大差值。
这里要分三种情况转移:设答案要取的最大值和最小值分别为
x
、
y
x 、 y
x、y ,则有
x
,
y
x,y
x,y在上半段、
x
,
y
x,y
x,y在下半段 和
x
x
x 在上半段
y
y
y 在下半段(因为最大值必须在最小值的后面)三种情况。
代码
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cmath>
using namespace std;
int n,x,y,k,kk,p,w[201000],lg[210000],dep[201000];
int f[200100][20],head[201000],maxn[201000][20],minn[201000][20],g[201000][20],q[200010][20];
struct c{
int x,next;
}a[201000];
void dfs(int x,int fa)
{
// cout<<x<<" "<<fa<<endl;
dep[x]=dep[fa]+1;
f[x][0]=fa;
minn[x][0]=min(w[x],w[fa]);
maxn[x][0]=max(w[x],w[fa]);
if(w[fa]>=w[x])
{
q[x][0]=1;
g[x][0]=w[fa]-w[x];
}
for(int i=1;i<=lg[dep[x]];i++)
{
if(q[x][i-1]||q[f[x][i-1]][i-1])
q[x][i]=1;
f[x][i]=f[f[x][i-1]][i-1];
maxn[x][i]=max(maxn[f[x][i-1]][i-1],maxn[x][i-1]);
minn[x][i]=min(minn[f[x][i-1]][i-1],minn[x][i-1]);
g[x][i]=max(g[f[x][i-1]][i-1],g[x][i-1]);//x,y在上半段、 x,y在下半段两种情况
g[x][i]=max(g[x][i],maxn[f[x][i-1]][i-1]-minn[x][i-1]);x在上半段 y 在下半段
}
for(int i=head[x];i;i=a[i].next)
dfs(a[i].x,x);
}
void add(int x,int y){
a[++k].x=y;
a[k].next=head[x];
head[x]=k;
}
int work(int x,int k){
int y=dep[x]-k+1,m1=2147483600,gg=0;
bool ok=0;
while(dep[x]>y)
{
int l=lg[dep[x]-y];
if(q[x][l])
ok=1;
gg=max(gg,g[x][l]);//g[x][l]相当于max(g[f[x][l-1][l-1],g[x][l-1]),也就是x,y在上半段、 x,y在下半段两种情况。
gg=max(gg,maxn[x][l]-m1);//x在上半段 y 在下半段
m1=min(m1,minn[x][l]);//保证y是最小的,不断向上找更大的x求差
x=f[x][l];
}
if(ok)return gg;
return 0;
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&x,&y);
add(y,x);
}
scanf("%d",&p);
lg[0]=-1;
for(int i=1;i<=n;i++)
lg[i]=lg[i>>1]+1;
dfs(1,0);
for(int i=1;i<=p;i++)
{
scanf("%d%d",&x,&kk);
printf("%d\n",work(x,kk));
}
return 0;
}