美团 CodeM 复赛」城市网络
内存限制:64 MiB时间限制:500 ms标准输入输出
题目描述
有一个树状的城市网络(即 nnn 个城市由 n−1n-1n−1 条道路连接的连通图),首都为 111 号城市,每个城市售卖价值为 aia_iai 的珠宝。
你是一个珠宝商,现在安排有 qqq 次行程,每次行程为从 uuu 号城市前往 vvv 号城市(走最短路径),保证 vvv 在 uuu 前往首都的最短路径上。
在每次行程开始时,你手上有价值为 ccc 的珠宝(每次行程可能不同),并且每经过一个城市时(包括 uuu 和 vvv),假如那个城市中售卖的珠宝比你现在手上的每一种珠宝都要优秀(价值更高,即严格大于),那么你就会选择购入。
现在你想要对每一次行程,求出会进行多少次购买事件。
输入格式
第一行,两个正整数 n,qn , qn,q。
第二行,nnn 个正整数 aia_iai 描述每个城市售卖的珠宝的价值。
接下来 n−1n-1n−1 行,每行描述一条道路 x,yx , yx,y (1≤x,y≤n1 \leq x , y \leq n1≤x,y≤n),表示有一条连接 x 和 y 的道路。
接下来 qqq 行,每行描述一次行程 u,v,cu , v , cu,v,c (1≤u,v≤n1 \leq u , v \leq n1≤u,v≤n)。
输出格式
对于每次行程输出一行,为所购买次数。
样例
样例输入
5 4
3 5 1 2 4
1 2
1 3
2 4
3 5
4 2 1
4 2 2
4 2 3
5 1 5
样例输出
2
1
1
0
数据范围与提示
对于 100%100 \%100% 的数据,保证 2≤n≤105,1≤q≤1052 \leq n \leq 10^5 , 1 \leq q \leq 10^52≤n≤105,1≤q≤105 , 1≤ai≤1051 \leq a_i \leq 10^51≤ai≤105 , 1≤c≤1051 \leq c \leq 10^51≤c≤105。
分析:有趣的技巧是把询问挂在起点下面当叶子;
对于新树上的每个点找第一个比他大的祖先,可以倍增实现;
对于一个点,第一个比他大的祖先得到之后又可以继续倍增;
这样这个问题就完美解决了;
总的来说:倍增就和二分一样 能找到最后满足<=某个值的最后一个值,接着你就能找到第一个父亲了
,至于一开始的0点,是不用管的,所有不满足的点都会到达0点,0点的深度最小所有不会有任何影响
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5+100;
vector<int> v[2*N+100];
int n,q;
int num[N];
int dep[N];
int fa[N][20];
int to[N];
void dfs(int x,int y)
{
int pos=y;
for(int i=19;i>=0;i--) if(fa[pos][i]&&num[fa[pos][i]]<=num[x]) pos=fa[pos][i];
//利用树上倍增的性质,因为父亲的父亲的值顺延上去,只会越来越大,具有单调性
//在x上面找到最后一个小于等于x的值
//那么后面的都是大于等于x的值
if(num[pos]>num[x]) fa[x][0]=y; //如果全部都比x大,那么x的第一个父亲就是y
else fa[x][0]=fa[pos][0]; //否则第一个比他大的就是最后一个比它小的点的第一个父亲
for(int i=1;fa[fa[x][i-1]][i-1];i++)//更新x走所有2^i 步的点
fa[x][i]=fa[fa[x][i-1]][i-1];
dep[x]=dep[y]+1; //更新x的高度
for(int i=0;i<v[x].size();i++)
{
if(v[x][i]==y) continue;
dfs(v[x][i],x);
}
}
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
{
scanf("%d",&num[i]);
}
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
v[a].push_back(b);
v[b].push_back(a);
}
for(int i=1;i<=q;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
num[n+i]=z;
v[n+i].push_back(x);
v[x].push_back(n+i);
to[n+i]=y;
}
dfs(1,0);
for(int i=n+1;i<=n+q;i++)
{
int ret=0,pos=i;
for(int j=19;j>=0;j--)
{
if(dep[fa[pos][j]]>=dep[to[i]]) //利用树上倍增的性质
//利用树上倍增的性质,因为父亲的父亲的值顺延上去,只会越来越大,具有单调性
//和树上lca是一样的,从低的点倍增到高的点
{
ret+=(1<<j);
pos=fa[pos][j];
}
}
printf("%d\n",ret);
}
}