鉄道旅行 (Railway Trip)

逆天贪心

设对于一个点 \(x\) 花费 \(d\) 步能走到的最左边为 \(L_{x,d}\),最右边为 \(R_{x,d}\)

主要证明一个东西:若 \((a,b)\) 这个询问中区间 \([L_{a,x},R_{a,x}]\)\([L_{b,y},R_{b,y}]\) 有交,那么必然存在一条步数为 \(x+y\) 的路径。

证明:

分类讨论

  • \(val_{R_{a,x}} < val_{L_{b,y}}\),此时显然可以到 \(L_{b,y}\)

  • \(val_{R_{a,x}} > val_{L_{b,y}}\),此时显然可以到 \(R_{a,x}\),并且一定能在 \(y\) 步之内到 \(b\)

\(Q.E.D\)

然后倍增即可,至于为何可以像代码中写的那样将两个端点分开考虑,仔细思考一下就容易发现这样得到的路径必定不劣。

点击查看代码
#include<bits/stdc++.h>
#define fir first
#define sec second
#define int long long
#define mkp(a,b) make_pair(a,b)
using namespace std;
typedef pair<int,int> pir; 
inline int read(){
	int x=0,f=1; char c=getchar();
	while(!isdigit(c)){if(c=='-') f=-1; c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
	return x*f;
}
const int mod=998244353,inf=1e18,N=1e5+5;
int n,k,q;
int a[N];
int l[N][20],r[N][20],st[N],top;
signed main(){
	n=read(),k=read(),q=read();
	for(int i=1;i<=n;i++) a[i]=read();
	top=0;
	for(int i=1;i<=n;i++){
		while(top&&a[st[top]]<a[i]) top--;
		if(top) l[i][0]=st[top];
		else l[i][0]=i;
		st[++top]=i;
	}
	top=0;
	for(int i=n;i>=1;i--){
		while(top&&a[st[top]]<a[i]) top--;
		if(top) r[i][0]=st[top];
		else r[i][0]=i;
		st[++top]=i;
	}
	for(int j=1;j<=19;j++) for(int i=1;i<=n;i++) 
	l[i][j]=min(l[l[i][j-1]][j-1],l[r[i][j-1]][j-1]),
	r[i][j]=max(r[r[i][j-1]][j-1],r[l[i][j-1]][j-1]);
	while(q--){
		int L=read(),R=read();
		if(L>R) swap(L,R);
		int ans=0;
		int x,y;
		x=y=L;
		for(int i=19;i>=0;i--){
			int pre=min(l[x][i],l[y][i]),nxt=max(r[x][i],r[y][i]);
			if(nxt<R){
				x=pre,y=nxt;
				ans+=(1<<i);
			}
		}
		L=y;
		x=y=R;
		for(int i=19;i>=0;i--){
			int pre=min(l[x][i],l[y][i]),nxt=max(r[x][i],r[y][i]);
			if(pre>L){
				x=pre,y=nxt;
				ans+=(1<<i);
			}
		}
		cout<<ans<<'\n';
	}
}
posted @ 2024-08-22 16:02  ~Cyan~  阅读(9)  评论(0编辑  收藏  举报