给定长 的序列 ,满足 。
构建一个 个点的无向图 如下:
- ,若存在某个正整数 满足 且 都有 ,则 中存在边 ;
次询问,每次询问点 和 之间的最短路长度减一。
,。
不妨称满足 且 都有 的边 为 。
不难发现若把边 看作区间 ,则:
- 任意两个区间要么包含要么相离;
- 对于同一个 ,所有 对应的区间的并为 (整个序列);
- 对于某条 ,,所有满足 的 对应的区间的并为 ;

那么这些边形成了类似树的结构:把每一条边看作一个点,则对于每一条 ,所有端点在 内的 都是它的儿子。
那么最短路的形态肯定是不断往父亲走,走到 后再往儿子走。
也就是说,设路径序列为 ,则:
- 存在一个分界点 ,满足 , 且 ,,即路径上的 先增再减;
- ,都有 ,否则把 加入 肯定不劣;
那么考虑倍增,不妨设 和 分别表示 长度为 且 时 可能的最小值和最大值,显然 都有 。
那么根据之前的结论,有:
而 和 分别为 左边和右边第一个 的 。
不妨令询问的 ,则每次询问:
- 从 开始在 的情况下不断跳 ,设最后跳到了 ;
- 从 开始在 的情况下不断跳 ;
时间复杂度 ,代码如下:
#include <iostream>
#include <cstdio>
using namespace std;
const int S=100005,BS=25;
int n,k,q,a[S];
int top,sta[S];
int lb[S][BS],rb[S][BS];
int main()
{
scanf("%d%d%d",&n,&k,&q);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
top=0;
for(int i=1;i<=n;i++)
{
while(top>0&&a[sta[top]]<a[i]) top--;
lb[i][0]=top==0?i:sta[top];
sta[++top]=i;
}
top=0;
for(int i=n;i>=1;i--)
{
while(top>0&&a[sta[top]]<a[i]) top--;
rb[i][0]=top==0?i:sta[top];
sta[++top]=i;
}
for(int j=1;j<=BS-3;j++)
{
for(int i=1;i<=n;i++)
{
int l=lb[i][j-1],r=rb[i][j-1];
lb[i][j]=min(lb[l][j-1],lb[r][j-1]);
rb[i][j]=max(rb[l][j-1],rb[r][j-1]);
}
}
while(q--)
{
int x,y;
scanf("%d%d",&x,&y);
if(x>y) swap(x,y);
int ans=0;
int l=x,r=x;
for(int i=BS-3;i>=0;i--)
{
if(max(rb[l][i],rb[r][i])<y)
{
ans+=1<<i;
int tl=l,tr=r;
l=min(lb[tl][i],lb[tr][i]);
r=max(rb[tl][i],rb[tr][i]);
}
}
int u=r;
l=y,r=y;
for(int i=BS-3;i>=0;i--)
{
if(min(lb[l][i],lb[r][i])>u)
{
ans+=1<<i;
int tl=l,tr=r;
l=min(lb[tl][i],lb[tr][i]);
r=max(rb[tl][i],rb[tr][i]);
}
}
printf("%d\n",ans);
}
return 0;
}