(纪中)3523. JIH的玩偶【倍增】

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


解题思路

其实刚看完题还是能想到倍增的,但是因为没推出样例就没打。。啊这。。
题目有两个很坑的点,

  1. “重要度大的客户必须在重要度小的客户后面”,意思是重要度大的客户的深度必须比重要度小的客户的深度小,所以我们求的重要程度相差最大的两个客户,大的要比小的的深度小。
  2. 从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][i1]][i1];
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][i1]][i1],maxn[x][i1]);
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][i1]][i1],minn[x][i1]);

接着,对于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][i1]q[f[x][i1]][i1])
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 xy ,则有 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值