牛客 城市网络(倍增 思维)

原题链接

题意:

在这里插入图片描述

思路:

  • 有一个关键信息就是 v v v一定是 u u u的祖先,也就是说从 u u u向根节点跳的时候一定可以到达 v v v
  • 考虑用倍增处理, d p [ i ] [ j ] dp[i][j] dp[i][j]表示从 i i i向根节点走并且购买了 2 j 2^j 2j件物品的位置
  • 转移也是正常的转移 d p [ i ] [ j ] = d p [ d p [ i ] [ j − 1 ] ] [ j − 1 ] dp[i][j]=dp[dp[i][j-1]][j-1] dp[i][j]=dp[dp[i][j1]][j1]
  • 关键在于 d p [ i ] [ 0 ] dp[i][0] dp[i][0]的确定, d p [ i ] [ 0 ] dp[i][0] dp[i][0]的含义就是 i i i向上走第一个大于 a [ i ] a[i] a[i]的位置
  • 如果 a [ f a ] > a [ u ] a[fa]>a[u] a[fa]>a[u]的话,那么 d p [ u ] [ 0 ] = f a dp[u][0]=fa dp[u][0]=fa
  • 不然,就从 d p [ f a ] [ j ] dp[fa][j] dp[fa][j]进行倍增来找到 d p [ u ] [ 0 ] dp[u][0] dp[u][0]
  • 处理 d p dp dp的过程用 d f s dfs dfs实现,所以在遍历到 u u u的时候,父节点 f a fa fa的信息已经保留了。从 f a fa fa向上跳,如果跳到某个点存在并且价值小于 u u u,跳到这个点后再向上跳;价值大于u的话,说明正确位置在该点下面的点,缩小 i i i继续跳。与正常的倍增一样, i i i也要从大到小枚举
  • 对于查询,可以将每次询问都增加一个节点 x x x u u u a [ x ] = c a[x]=c a[x]=c,这样方便处理。然后从 x x x v v v跳,每次从大到小枚举 i i i,比较深度。由于每个数一定能拆成二进制数,所以一定可以跳到 v v v;每次的答案增加 2 i 2^i 2i
  • 注意:要开两倍数组

代码:

const int maxn=2e5+7;

vector<int>g[maxn];
int n,q,a[maxn],dep[maxn],pos[maxn],dp[maxn][25];

void dfs(int u,int fa){
	dep[u]=dep[fa]+1;
	if(a[fa]>a[u]){
		dp[u][0]=fa;
	}
	else{
		int x=fa;
		for(int i=20;i>=0;i--)
			if(dp[x][i]&&a[dp[x][i]]<=a[u]){
//跳到这个点存在并且价值小于u,跳到这个点再向上跳;价值大于u的话,说明在该点下面的点,缩小i继续跳。
				x=dp[x][i];
			}
		dp[u][0]=dp[x][0];
	}
	for(int i=1;i<=20;i++)
		dp[u][i]=dp[dp[u][i-1]][i-1];
	for(auto t:g[u]){
		if(t==fa) continue;
		dfs(t,u);
	}
}

int main(){
	n=read,q=read;
	rep(i,1,n) a[i]=read;
	rep(i,1,n-1){
		int u=read,v=read;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	rep(i,1,q){
		int u=read,v=read,c=read;
		a[n+i]=c;
		g[n+i].push_back(u);
		g[u].push_back(n+i);
		pos[i]=v;
	}
	dfs(1,0);
	rep(i,1,q){
		int u=n+i,v=pos[i],ans=0;
		for(int i=20;i>=0;i--)
			if(dep[dp[u][i]]>=dep[v]){
				ans+=(1<<i);u=dp[u][i];
			}
		printf("%d\n",ans);
	}
	return 0;
}



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

豆沙睡不醒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值