城市网络【倍增】【DP】

>Link

ybtoj城市网络


>Description

有一个树状的城市网络(即 N N N 个城市由 N − 1 N-1 N1 条道路连接的连通图),首都为 1 1 1 号城市,每个城市售卖价值为 a i a_i ai 的珠宝。

你是一个珠宝商,现在安排有 q q q 次行程,每次行程为从 u u u 号城市前往 v v v 号城市(走最短路径),保证 v v v u u u 前往首都的最短路径上。

在每次行程开始时,你手上有价值为 c c c 的珠宝(每次行程可能不同),并且每经过一个城市时(包括 u u u v v v ),假如那个城市中售卖的珠宝比你现在手上的每一种珠宝都要优秀(价值更高,即严格大于),那么你就会选择购入。

现在你想要对每一次行程,求出会进行多少次购买事件。

n , q ≤ 1 0 5 n,q\le10^5 n,q105


>解题思路

考虑从一个节点往上倍增, g i , j g_{i,j} gi,j 表示从 i i i 节点往上购买 2 j 2^j 2j 次到达的节点,如果我们知道了 g i , 0 g_{i,0} gi,0 就可以求出所有了(倍增的精髓)
显然 g i , 0 g_{i,0} gi,0 就是求 i i i 节点从下往上第一个大于 a i a_i ai 的节点,这也可以用倍增来完成,用一个 m a x n i , j maxn_{i,j} maxni,j 来维护从 i i i 节点往上 2 j 2^j 2j 步这中间的最大值
然后对于每个询问,我们从 u u u 不断往上跳 g g g,注意不要超过 v v v,累计答案就好了


>代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
using namespace std;

struct edge
{
    
    
	int to, nxt;
} e[N * 2];
int n, q, a[N], cnt, h[N], dep[N], fa[N][50], maxn[N][50], g[N][50];

void add (int u, int v)
{
    
    
	e[++cnt] = (edge){
    
    v, h[u]}; h[u] = cnt;
	e[++cnt] = (edge){
    
    u, h[v]}; h[v] = cnt;
}
void dfs (int now, int fath)
{
    
    
	dep[now] = dep[fath] + 1;
	fa[now][0] = fath;
	maxn[now][0] = max (a[now], a[fath]);
	for (int i = 1; i <= 20; i++)
	{
    
    
		fa[now][i] = fa[fa[now][i - 1]][i - 1];
		maxn[now][i] = max (maxn[now][i - 1], maxn[fa[now][i - 1]][i - 1]);
	}
	int x = now;
	for (int i = 20; i >= 0; i--)
	  if (maxn[x][i] <= a[now]) x = fa[x][i];
	g[now][0] = fa[x][0];
	for (int i = 1; i <= 20; i++)
	  g[now][i] = g[g[now][i - 1]][i - 1];
	for (int i = h[now]; i; i = e[i].nxt)
	  if (e[i].to != fath) dfs (e[i].to, now);
}
int ask (int x, int y, int c)
{
    
    
	int ret = 0;
	if (a[x] <= c)
	{
    
    
		for (int i = 20; i >= 0; i--)
		  if (maxn[x][i] <= c) x = fa[x][i];
		x = fa[x][0];
	}
	if (!x || dep[x] < dep[y]) return 0;
	ret = 1;
	for (int i = 20; i >= 0; i--)
	  if (g[x][i] && dep[g[x][i]] >= dep[y])
	    ret += 1 << i, x = g[x][i];
	return ret;
}

int main()
{
    
    
	freopen ("network.in", "r", stdin);
	freopen ("network.out", "w", stdout);
	int x, y, c;
	scanf ("%d%d", &n, &q);
	for (int i = 1; i <= n; i++) scanf ("%d", &a[i]);
	for (int i = 1; i < n; i++)
	{
    
    
		scanf ("%d%d", &x, &y);
		add (x, y);
	}
	dfs (1, 0);
	while (q--)
	{
    
    
		scanf ("%d%d%d", &x, &y, &c);
		printf ("%d\n", ask (x, y, c));
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43010386/article/details/121220264
今日推荐