【JOISC 2017】火车旅行(倍增)

本文介绍了一道JOISC2017竞赛题“火车旅行”的解题思路,采用倍增算法求解从一车站到另一车站间的最少停靠次数,通过预处理最左与最右可达站点来加速查询过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:【JOISC 2017】火车旅行

题目大意:有 n n 个车站,每个车站有一个编号,第 i 种车只会经过编号 i ≥ i 的站台。有 q q 次询问,每次询问从 i 号站台到 j j 号站台,在最优的乘车方案中,列车会停靠几次。

l[k][i] 表示从 i i 号车站出发,经过 2k 次停靠能够到达的最左侧的站点, r[k][i] r [ k ] [ i ] 表示从 i i 号车站出发,经过 2k 次停靠能够到达的最右侧的站点。我们发现每个状态只可能从最左侧和最右侧的车站转移过来,则状态转移方程为:


l[k][i]=min(l[k1][l[k1][i]],l[k1][r[k1][i]]) l [ k ] [ i ] = m i n ( l [ k − 1 ] [ l [ k − 1 ] [ i ] ] , l [ k − 1 ] [ r [ k − 1 ] [ i ] ] )

r[k][i]=max(r[k1][r[k1][i]],r[k1][l[k1][i]]) r [ k ] [ i ] = m a x ( r [ k − 1 ] [ r [ k − 1 ] [ i ] ] , r [ k − 1 ] [ l [ k − 1 ] [ i ] ] )


对于每个询问,还是因为每个状态只可能从最左侧和最右侧的车站转移过来,所以按类似之前的方法倍增即可。时间复杂度 Θ((n+q)log2n) Θ ( ( n + q ) log 2 ⁡ n ) 。详见代码。

#include <cstdio>
#include <iostream>
using namespace std;
const int logn = 17;
const int maxn = 100005;
int n, q, a[maxn], sz, st[maxn];
int l[maxn][logn], r[maxn][logn]; 
int main() {
    scanf("%d %*d %d", &n, &q);
    for (int i = 1; i <= n; i++) {
        scanf("%d", a + i);
    }
    st[sz = 0] = 1;
    for (int i = 1; i <= n; i++) {
        while (sz && a[st[sz]] < a[i]) sz--;
        l[i][0] = st[sz], st[++sz] = i;
    }
    st[sz = 0] = n;
    for (int i = n; i >= 1; i--) {
        while (sz && a[st[sz]] < a[i]) sz--;
        r[i][0] = st[sz], st[++sz] = i;
    }
    for (int k = 0; k < 16; k++) {
        for (int i = 1; i <= n; i++) {
            l[i][k + 1] = min(l[l[i][k]][k], l[r[i][k]][k]);
            r[i][k + 1] = max(r[r[i][k]][k], r[l[i][k]][k]);
        }
    }
    int x, y, L, R, lb, rb, ans;
    while (q--) {
        scanf("%d %d", &x, &y);
        if (x > y) {
            swap(x, y);
        }
        ans = 0;
        L = R = x;
        for (int i = 16; i >= 0; i--) {
            lb = min(l[L][i], l[R][i]);
            rb = max(r[L][i], r[R][i]);
            if (rb < y) {
                L = lb, R = rb;
                ans += 1 << i;
            }
        }
        x = R;
        L = R = y;
        for (int i = 16; i >= 0; i--) {
            lb = min(l[L][i], l[R][i]);
            rb = max(r[L][i], r[R][i]);
            if (lb > x) {
                L = lb, R = rb;
                ans += 1 << i;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值