[JOISC 2017] 火车旅行 / Railway Trip 题解

[JOISC 2017] 火车旅行 / Railway Trip

挺好的题目。

首先发现似乎是要求一个最短路,但是有 \(O(n^2)\) 条边太多了。发现,每个点只用向左右第一个比他大的点连边权为 \(1\) 的边即可,其他的边显然都可以用这些边组合出来,这可以单调栈处理出来。

然后考虑怎么快速求两点间最短路,考虑倍增,设 \(L_{i,j}\) 表示 \(i\) 走 \(2^j\) 步后能到的最左端点,\(R_{i,j}\) 为最右端点,怎么转移呢,因为可达的一个区间范围内,从端点走肯定不劣于从区间内部出发,所以 \(L_{i,j} = \min(L_{L_{i,j-1},j-1},L_{R_{i,j-1},j-1})\),右边同理。

每次询问就让起点和终点相互逼近就好了。

时间复杂度 \(O(n\log n)\)。

#include<bits/stdc++.h>
using namespace std;
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
inline int read(){
    int k=0,f=1,c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())k=k*10+(c^48);
    return k*f;
}
inline void write(int x){
    if(x<10)putchar(x+'0');
    else write(x/10),putchar(x%10+'0');
}
int n,k,m;
const int N = 1e5+10,inf=1e9;
int a[N],L[N][23],R[N][23],st[N],top;
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    n=read(),k=read(),m=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
    }
    for(int i=1;i<=n;i++){
        while(top&&a[st[top]]<a[i])top--;
        L[i][0]=st[top];
        st[++top]=i;
    }
    top=0;
    for(int i=n;i>=1;i--){
        while(top&&a[st[top]]<a[i])top--;
        R[i][0]=st[top];
        st[++top]=i;
    }
    for(int i=1;i<=n;i++){
        if(!L[i][0])L[i][0]=1;
        if(!R[i][0])R[i][0]=n;
    }
    for(int j=1;j<=21;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]);
        }
    }
    for(int i=1;i<=m;i++){
        int a,b;
        a=read(),b=read();
        if(a==b){
            write(0);putchar(endl);
            continue;
        }
        if(a>b)swap(a,b);
        pii l={a,a},r={b,b};
        int ans=0;
        for(int j=21;j>=0;j--){
            int ll=min(L[l.fst][j],L[l.scd][j]);
            int rr=max(R[l.fst][j],R[l.scd][j]);
            if(rr<b){
                ans+=(1ll<<j);
                l={ll,rr};
            }
        }
        for(int j=21;j>=0;j--){
            int ll=min(L[r.fst][j],L[r.scd][j]);
            int rr=max(R[r.fst][j],R[r.scd][j]);
            if(ll>l.scd){
                ans+=(1ll<<j);
                r={ll,rr};
            }
        }
        write(ans);putchar(endl);
    }
    return 0;
}
posted @ 2026-10-22 17:51  farenara_sayowell  阅读(3)  评论(0)    收藏  举报