其他分享
首页 > 其他分享> > P7599 [APIO2021] 雨林跳跃

P7599 [APIO2021] 雨林跳跃

作者:互联网

封闭道路的题解呢?

考虑 A = B , C = D A=B,C=D A=B,C=D

只需要从 A A A开始每一步都跳最大的那个就好了!如果能跳到,说明 m a x [ B , C − 1 ] < m a x [ C , D ] max[B,C-1]< max[C,D] max[B,C−1]<max[C,D]

否则我们要么一步跳过去,要么中途跳到 B , C − 1 B,C-1 B,C−1某个数然后自闭掉

那么我们只需要每次跳小于 max ⁡ [ C , D ] \max[C,D] max[C,D]范围内最大的一个就好了

然后考虑 A ≠ B A\not = B A​=B

我们想法是选择一个最大的 P ∈ [ A , B ] P\in [A,B] P∈[A,B],然后发现如果 P > max ⁡ [ C , D ] P>\max [C,D] P>max[C,D]就彻底完蛋了…

于是发现我们如果有一个值大于 max ⁡ [ C , D ] \max[C,D] max[C,D],这个值前面的所有数都达不到目标点了…

然后我们相当于在这个区间的一个后缀中找到一个最大的 < max ⁡ [ C , D ] <\max[C,D] <max[C,D] 的数

线段树二分就好了!寻找这个区间最后一个大于等于x的值,然后再在这个值前面查询一个最大值的位置就好了

然后考虑 C ≠ D C\not =D C​=D

我们发现有效的值似乎只有 max ⁡ [ C , D ] \max [C,D] max[C,D]

但是考虑我们可能没有跳到最大值的时候就已经跳过去了…

于是发现我们还是要从 [ B , C − 1 ] [B,C-1] [B,C−1]这个区间入手,我们第一次跳到这个区间的最大值的时候就结束了!因为我们下一步一定可以跳进目标区间!

于是考虑我们现在完全将所有问题都规约到了 A = B , C = D A=B,C=D A=B,C=D

如何解决这个问题的最小步数?

首先,第一个数在树上倍增,找到第一个小于等于他的最大的数,满足这个数再向左边跳就跳到大于它的位置了,如果直接跳到那还挺好的

然后,第二个树在序列上倍增,相当于我们每个数只能向右侧跳,然后计算这样跳到它的最小步数

就做完了,复杂度一个 log ⁡ \log log

注意我们可能一步跳到 [ C , D ] [C,D] [C,D]的不只有 [ B , C − 1 ] [B,C-1] [B,C−1]的max,还有这个max值的前驱我们可能一步跳到他

特别的,你会发现一开始找到的数就能一步跳过去就直接选择那个数就好啦!

P7598 [APIO2021] 六边形领域

转换成网格之后一三象限距离为切比雪夫距离二四象限曼哈顿距离

然后转换方法为六个方向变成上下左右右上方左下方

并且一个正方形要变成两部分!就可以对应原来的六边形网格了

#include <bits/stdc++.h>
#include <vector>
#include <iostream>
#include <cstring>
using std::max;
using std::min;

void init(int N, std::vector<int> H);
int minimum_jumps(int A, int B, int C, int D);
const int MAXN=3e5+7;
const int B=19;
int n;
int lg2[MAXN],f[B][MAXN],a[MAXN],g[B][MAXN],p[B][MAXN];
inline int qrym(int l,int r) {
	int tmp=lg2[r-l+1];
	return max(f[tmp][l],f[tmp][r-(1<<tmp)+1]);
}
int home[MAXN],nxt[MAXN],to[MAXN],ccnt,pre[MAXN],suf[MAXN];
inline void ct(int x,int y) {
	ccnt++;
	nxt[ccnt]=home[x];
	to[ccnt]=y;
	home[x]=ccnt;
}
inline void dfs(int u) {
	for(int i=1; i<B; ++i) {
		g[i][u]=g[i-1][g[i-1][u]];
	}
	for(int i=home[u]; i; i=nxt[i]) {
		dfs(to[i]);
	}
	return ;
}
inline void dfs2(int u) {
	for(int i=1; i<B; ++i)p[i][u]=p[i-1][p[i-1][u]];
	for(int i=home[u]; i; i=nxt[i])dfs2(to[i]);
	return ;
}
inline void init2() {
	pre[1]=0;
	for(int i=2; i<=n; ++i) {
		int l=1,r=i-1,mid,ans=0;
		while(l<=r) {
			mid=(l+r)>>1;
			if(qrym(mid,i-1)>=a[i]) {
				l=mid+1;
				ans=mid;
			} else {
				r=mid-1;
			}
		}
		pre[i]=ans;//前面第一个大于它的位置
	}
	a[n+1]=a[0]=0;
	suf[n]=n+1;
	for(int i=n-1; i>=1; --i) {
		int l=i+1,r=n,mid,ans=n+1;
		while(l<=r) {
			mid=(l+r)>>1;
			if(qrym(i+1,mid)>=a[i]) {
				r=mid-1;
				ans=mid;
			} else {
				l=mid+1;
			}
		}
		suf[i]=ans;
	}
	int mx=0;
	for(int i=1; i<=n; ++i) {
		if(a[i]>a[mx])mx=i;
		if(a[suf[i]]>a[pre[i]]) {
			g[0][i]=suf[i];
			ct(suf[i],i);
		} else if(a[suf[i]]<a[pre[i]]) {
			g[0][i]=pre[i];
			ct(pre[i],i);
		}
	}
	dfs(mx);
	ccnt=0;
	memset(home,0,sizeof(home));
	for(int i=1; i<=n; ++i) {
		p[0][i]=suf[i];
		ct(suf[i],i);
	}
	dfs2(n+1);
	return ;
}

inline int getpos1(int L,int R,int p) {
	int mid,ans=0;
	while(L<=R) {
		mid=(L+R)>>1;
		if(qrym(mid,R)>=p) {
			ans=mid;
			L=mid+1;
		} else {
			R=mid-1;
		}
	}
	return ans;
}

inline int getpos2(int L,int R,int p) {
	int mid,ans=0;
	while(L<=R) {
		mid=(L+R)>>1;
		if(qrym(L,mid)>=p) {
			ans=mid;
			R=mid-1;
		} else {
			L=mid+1;
		}
	}
	return ans;
}

void init(int N, std::vector<int> H) {
	n=N;
	for(int i=2; i<=N; ++i) {
		lg2[i]=lg2[i>>1]+1;//我太菜了
	}
	for(int i=1; i<=N; ++i) {
		a[i]=f[0][i]=H[i-1];
	}
	for(int i=1; i<B; ++i) {
		for(int j=1; j<=N; ++j) {
			if(j+(1<<i)-1<=N) {
				f[i][j]=max(f[i-1][j],f[i-1][j+(1<<(i-1))]);
			}
		}
	}
	init2();
	return ;
}

inline int calc(int x,int y) {
	int ret=0;
	for(int i=B-1; i>=0; --i) {
		if(a[g[i][x]] && a[g[i][x]]<=a[y]) {
			ret+=(1<<i);
			x=g[i][x];
		}//先跳大的qwq
	}
	if(x==y)return ret;
	for(int i=B-1; i>=0; --i) {
		if(a[p[i][x]] && a[p[i][x]]<=a[y]) {
			ret+=(1<<i);
			x=p[i][x];
		}//再跳小的qwq
	}
	return ret;
}

int minimum_jumps(int A, int B, int C, int D) {
	++A,++B,++C,++D;
	if(B>=C)return 0;
	int Lim=qrym(B,C-1);//QAQ!
	int mx2=qrym(C,D);
	if(Lim>mx2)return -1;
	int l=A,r=B,mid,ans=A-1;//QAQ
	while(l<=r) {
		mid=(l+r)>>1;
		if(qrym(mid,B)>=mx2) {//???
			ans=mid;
			l=mid+1;
		} else {
			r=mid-1;
		}
	}
	if(ans==B)return 1;//一次杀掉了
	int mx1=qrym(ans+1,B);
	if(mx1>Lim)return 1;
	int u=getpos1(ans+1,B,mx1);//直接gepos
	int v=getpos2(B,C-1,Lim);//u->v
	int ans1=calc(u,v);
	int ans2=1e9;
	if(pre[v] && a[pre[v]]<mx2)ans2=calc(u,pre[v]);
	ans1=min(ans1,ans2)+1;
	return ans1;
}

标签:return,qrym,int,max,P7599,mid,雨林,APIO2021,ans
来源: https://blog.csdn.net/xiaxiaoguang_/article/details/118555393