【Ybtoj】【NOIP2021 模拟赛 B 组 Day8】D. 城市网络【倍增】

102 篇文章 1 订阅
9 篇文章 0 订阅

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


解题思路

g i , j g_{i,j} gi,j表示从 i 节点往上购买 2 j 2^j 2j 次到达的节点,如果我们知道了 g i , 0 g_{i,0} gi,0就可以求出所有了(倍增的精髓)
g i , 0 g_{i,0} gi,0 就是求 i 节点从下往上第一个大于 a i a_i ai 的节点,这也可以用倍增来完成,用一个 m a x n i , j maxn_{i,j} maxni,j来维护从 i 节点往上 2 j 2^j 2j步这中的最大值,若 m a x n i , j < a i maxn_{i,j}<a_i maxni,j<ai,则 i = f i , j i=f_{i,j} i=fi,j,i 一直往上跳。最后第一个大于 a i a_i ai 的节点为 f i , 0 f_{i,0} fi,0

最后计算答案的时候,若 c > a u c>a_u c>au,要先找到以一个大于c的节点 u , a n s + + u,ans++ uans++,再从那里开始往上跳,枚举j,即枚举往上购买 2 j 2^j 2j 次,若 g u , j g_{u,j} gu,j 没有超过 v , a n s + = 2 j v,ans+=2^j vans+=2j


代码

#include <bits/stdc++.h>
#define ll long long
#define N 100010
using namespace std;

int n,q,u,v,s,k,ans;
int head[N*2],w[N],f[N][25],g[N][25],dep[N],maxn[N][25],lg[N];

struct c {
	int x,next;
} a[N*2];

void add(int x,int y) {
	a[++k]=(c) {
		y,head[x]};
	head[x]=k;
}

void dfs(int x,int fa) {
	dep[x]=dep[fa]+1;
	f[x][0]=fa;
	maxn[x][0]=max(w[x],w[fa]);
	for(int i=1; i<=20; i++) {
		f[x][i]=f[f[x][i-1]][i-1];
		maxn[x][i]=max(maxn[x][i-1],maxn[f[x][i-1]][i-1]);
	}
	int xx=x;
	for(int i=20; i>=0; i--) {
		if(maxn[xx][i]<=w[x])
			xx=f[xx][i];
	}
	g[x][0]=f[xx][0];
	for(int i=1; i<=20; i++)
		g[x][i]=g[g[x][i-1]][i-1];
	for(int i=head[x]; i; i=a[i].next) {
		int y=a[i].x;
		if(y==fa)continue;
		dfs(y,x);
	}
}

int main() {
	freopen("network.in","r",stdin);
	freopen("network.out","w",stdout);
	scanf("%d%d",&n,&q);
	lg[0]=-1;
	for(int i=1; i<=n; i++)
		lg[i]=lg[i>>1]+1;
	for(int i=1; i<=n; i++)
		scanf("%d",&w[i]);
	for(int i=1; i<=n-1; i++) {
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	dfs(1,0);
	for(int i=1; i<=q; i++) {
		scanf("%d%d%d",&u,&v,&s);
		ans=1;
		if(w[u]<=s)
		{
			for(int j=20;j>=0;j--)
			{
				if(maxn[u][j]<=s)
					u=f[u][j];
			}
			u=f[u][0];
		}
		if(!u||dep[u]<dep[v]){
			printf("0\n");
			continue;	
		}
		for(int j=20; j>=0; j--) {
			if(g[u][j]&&dep[g[u][j]]>=dep[v])
			{
				u=g[u][j];
				ans=ans+pow(2,j);
			}
		}
		printf("%d\n",ans);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值