题解:AT_joisc2017_f 鉄道旅行 (Railway Trip)

题意

鉄道旅行 (Railway Trip)

分析

非常神仙的倍增做法。

我们设 li,j 表示从 i 点出发,停靠 2j 站后能抵达的最左位置。

同理设 ri,j 表示从 i 点出发,停靠 2j 站后能抵达的最右位置。

考虑如何更新这两个状态。

因为可以走回头路,所以简单的 li,j=lli,j1,j1ri,j=rri,j1,j1 这种倍增方法是不行的。

我们可以考虑从两个端点来更新。

递推式如下:

li,j=min(lli,j1,j1,lri,j1,j1)ri,j=max(rli,j1,j1,rri,j1,j1)

每次查询时先从较左点向两侧扩展区间,如果该区间没有包含较右点,那么就扩展区间并统计答案。

然后再从较右点向两侧扩展区间,如果两区间没有交,那么扩展区间并统计答案。

最后得到的两区间再扩展一步就一定会有交。

Code

#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
int lis[maxn], l[maxn][18], r[maxn][18], stk[maxn], *top=stk;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int n, k, q;
cin>>n>>k>>q;
for(int i=1;i<=n;i++) cin>>lis[i];
for(int i=1;i<=n;i++)
{
while(top!=stk&&lis[*top]<lis[i]) top--;
if(top!=stk) l[i][0]=*top;
else l[i][0]=i;
*++top=i;
}
top=stk;
for(int i=n;i;i--)
{
while(top!=stk&&lis[*top]<lis[i]) top--;
if(top!=stk) r[i][0]=*top;
else r[i][0]=i;
*++top=i;
}
for(int j=1;j<18;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[l[i][j-1]][j-1], r[r[i][j-1]][j-1]);
int a, b, ans;
while(q--)
{
cin>>a>>b;
ans=0;
if(a>b) swap(a, b);
int lx=a, rx=a;
for(int i=17, nl, nr;~i;i--)
{
nl=min(l[lx][i], l[rx][i]);
nr=max(r[lx][i], r[rx][i]);
if(nr<b) lx=nl, rx=nr, ans+=1<<i;
}
a=rx;
lx=rx=b;
for(int i=17, nl, nr;~i;i--)
{
nl=min(l[lx][i], l[rx][i]);
nr=max(r[lx][i], r[rx][i]);
if(nl>a) lx=nl, rx=nr, ans+=1<<i;
}
cout<<ans<<'\n';
}
}

本文作者:redacted-area

本文链接:https://www.cnblogs.com/redacted-area/p/18379540

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Jimmy-LEEE  阅读(9)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起