2021牛客多校1——J:Journey of Railway Stations(线段树)

题面

题意:

  • 一段路上有 N N N​​​​ 个火车站,每个火车站有一个合法时间段 [ u i , v i ] [u_i, v_i] [ui,vi]​​​​​,相邻两个火车站有一个长度 c o s t [ i ] cost[i] cost[i](代表从 i i i i − 1 i-1 i1的路径长度)。每次问,在 u i u_i ui 的时间从 i i i 出发后,能否依次经过 i + 1 i+1 i+1~ j j j​​​​​​ 的所有点,使得到达时间满足每个火车站的合法区间(如果提前到可以等待,迟到了失败了)。同时还可能修改一段路的长度,或者修改一个点的合法时间段。
  • 操作1:询问火车从 l l l出发,依次经过 l l l~ r r r的点能不能保证每个点到达时间都满足其合法区间(具体满足规则见上)
  • 操作2:修改 i i i i − 1 i-1 i1的路径长度为 w w w
  • 操作3:修改 i i i​到 i − 1 i-1 i1​的合法时间端为 [ p , q ] [p, q] [p,q]

思路:

  • 我们利用线段树维护三个数值,一个 r e s res res​代表下一个节点是否可以到达,一个 x x x​代表这个节点到达下一节点的最早时间,一个 y y y​​代表到达当前节点允许的最晚时间。

  • 对于单个节点 p p p​​​,它到达下一节点的最早时间就是从当前节点最早出发加上从 p p p p + 1 p+1 p+1所要花费的时间,即为: x = u [ p ] + c o s t [ p ] x=u[p]+cost[p] x=u[p]+cost[p]

    它到达当前节点允许的最晚时间也直接为 v [ p ] v[p] v[p],并且当前可达, r e s res res标记为1。

  • 对于区间合并,当前区间的到达下一个节点的最早时间如果大于下一个区间允许到达的最晚时间,那么就证明这两个区间是不可达到的,则将 r e s res res更新为1,或者其中任意一个节点是不可达的,当前合并区间的 r e s res res也应该为不可达的。

  • 对于区间合并,更新区间和的 x x x​​,对于前面的区间,如果加上后面的路程总花费与后面区间到达下一节点的最早时间取最大值的结果,即 x p = m i n ( u i + c o s t [ u j ] , u j ) x_p=min(u_i+cost[u_j],u_j) xp=min(ui+cost[uj],uj)​,就得到的就是当前这个区间中到达下一个点的最早时间。

  • 对于区间合并,更新区间和的 y y y,对于前面的区间,如果前面区间的最晚达到时间与后面的最晚到达时间减去前面需要走的路径取最小值,即 y p = m i n ( v i , v j − c o s t [ v i ] ) y_p=min(v_i,v_j-cost[v_i]) yp=min(vi,vjcost[vi]),就得到的就是当前这个区间中允许到达当前区间的最晚时间。

  • 操作2和操作3就是线段树的单点修改操作。

代码:

#include <bits/stdc++.h>
using namespace std;
#define ls (rt * 2)
#define rs (rt * 2 + 1)
const int MAXN = 1e6 + 10;
int u[MAXN];
int v[MAXN];
int cost[MAXN];
// res:次节点能否通过
// u:这个节点到达下一站的最早时间
// v:到达这个节点允许的最晚时间
struct node {
    int res, sum, u, v;
} tree[MAXN * 4];
node merge(node a, node b) {
    node c;
    c.res = a.res & b.res;
    if (a.u > b.v) {
        c.res = 0;
    }
    c.sum = a.sum + b.sum;
    c.u = max(a.u + b.sum, b.u);
    c.v = min(a.v, b.v - a.sum);
    return c;
}
void build(int rt, int l, int r) {
    if (l == r) {
        tree[rt].res = 1;
        tree[rt].sum = cost[l];
        tree[rt].u = u[l] + cost[l];
        tree[rt].v = v[l];
        return;
    }
    int mid = l + r >> 1;
    build(ls, l, mid);
    build(rs, mid + 1, r);
    tree[rt] = merge(tree[ls], tree[rs]);
}
void modify(int rt, int l, int r, int id) {
    if (l == r) {
        tree[rt].res = 1;
        tree[rt].sum = cost[l];
        tree[rt].u = u[l] + cost[l];
        tree[rt].v = v[l];
        return;
    }
    int mid = l + r >> 1;
    if (id <= mid) {
        modify(ls, l, mid, id);
    } else {
        modify(rs, mid + 1, r, id);
    }
    tree[rt] = merge(tree[ls], tree[rs]);
}
node query(int rt, int l, int r, int L, int R) {
    if (l == L && r == R) {
        return tree[rt];
    }
    int mid = l + r >> 1;
    if (R <= mid) {
        return query(ls, l, mid, L, R);
    } else if (L > mid) {
        return query(rs, mid + 1, r, L, R);
    } else {
        return merge(query(ls, l, mid, L, mid), query(rs, mid + 1, r, mid + 1, R));
    }
}
int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        int n;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &u[i]);
        }
        for (int i = 1; i <= n; i++) {
            scanf("%d", &v[i]);
        }
        for (int i = 1; i < n; i++) {
            scanf("%d", &cost[i]);
        }
        build(1, 1, n);
        int q;
        scanf("%d", &q);
        while (q--) {
            int op;
            scanf("%d", &op);
            if (op == 0) {
                int l, r;
                scanf("%d %d", &l, &r);
                if (query(1, 1, n, l, r).res) {
                    cout << query(1, 1, n, l, r).u << " " << query(1, 1, n, l, r).v << endl;
                    puts("Yes");
                } else {
                    puts("No");
                }
            }
            if (op == 1) {
                int k, w;
                scanf("%d %d", &k, &w);
                cost[k] = w;
                modify(1, 1, n, k);
            }
            if (op == 2) {
                int k, p, q;
                scanf("%d %d %d", &k, &p, &q);
                u[k] = p;
                v[k] = q;
                modify(1, 1, n, k);
            }
        }
    }
    return 0;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kunyuwan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值