2021牛客暑期多校训练营1 J(线段树)

题目大意:

  一段路上有 N N N 个点,每个点有一个合法时间段 [ u i , v i ] [u_i, v_i] [ui,vi],相邻两个点有一个长度。每次问,在 u i u_i ui 的时间从 i 出发后,能否依次经过 i + 1 i+1 i+1 ~ j j j 的所有点,使得到达时间满足每个点的合法区间(如果提前到可以等待,迟到了失败了)。同时还可能修改一段路的长度,或者修改一个点的合法时间段。
   N , Q < = 1000000 N, Q <= 1000000 N,Q<=1000000

解题思路:
  • 考虑 (l, r) 的询问所有的限制条件。我们以 (1, 3) 举例,即:
    𝑢 1 ≤ 𝑣 1 , m a x ( 𝑢 1 + 𝑑 1 , 𝑢 2 ) ≤ 𝑣 2 , m a x ( m a x ( 𝑢 1 + 𝑑 1 , 𝑢 2 ) + 𝑑 2 , 𝑢 3 ) ≤ 𝑣 3 𝑢_1≤𝑣_1,max(𝑢_1+𝑑_1,𝑢_2 )≤𝑣_2,max(max(𝑢_1+𝑑_1,𝑢_2 )+𝑑_2, 𝑢_3 )≤𝑣_3 u1v1,max(u1+d1,u2)v2,max(max(u1+d1,u2)+d2,u3)v3
    现在我们设 𝑢 𝑖 ′ = 𝑢 𝑖 + 𝑑 𝑖 + 𝑑 ( 𝑖 + 1 ) + … + 𝑑 ( 𝑛 − 1 ) , 𝑣 𝑖 ′ = 𝑣 𝑖 + 𝑑 𝑖 + 𝑑 ( 𝑖 + 1 ) + … + 𝑑 ( 𝑛 − 1 ) 𝑢_𝑖^′=𝑢_𝑖+𝑑_𝑖+𝑑_(𝑖+1)+…+𝑑_(𝑛−1),𝑣_𝑖^′=𝑣_𝑖+𝑑_𝑖+𝑑_(𝑖+1)+…+𝑑_(𝑛−1) ui=ui+di+d(i+1)++d(n1),vi=vi+di+d(i+1)++d(n1)
    现在 (l, r) 的限制变成了:
    𝑢 1 ′ ≤ 𝑣 1 ′ , m a x ( 𝑢 1 ′ , 𝑢 2 ′ ) ≤ 𝑣 2 ′ , m a x ( 𝑢 1 ′ , 𝑢 2 ′ , 𝑢 3 ′ ) ≤ 𝑣 3 ′ 𝑢_1^′≤𝑣_1^′,max(𝑢_1^′,𝑢_2^′ )≤𝑣_2^′,max(𝑢_1^′, 𝑢_2^′,𝑢_3^′ )≤𝑣_3^′ u1v1,max(u1,u2)v2,max(u1,u2,u3)v3
    线段树维护当前区间是否可行、u 的最大值和 v 的最小值,复杂度是 𝑂 ( 𝑁 l o g 𝑁 ) 𝑂(𝑁 log 𝑁) O(NlogN)
  • 合并区间时,要两个子区间都可行且左子区间的u最大值小于右子区间的v的最小值,当前区间才可行,否则不可行
  • 当更新 u i , v i u_i,v_i ui,vi时只要单点修改就行了
  • 当更新 d i d_i di时,要对 1 1 1~ i i i进行区间修改即可
AC代码
#include <bits/stdc++.h>
#define endl "\n"
using namespace std;
const int maxn = 1e6 + 10;
typedef long long ll;
const ll inf = 0x3f3f3f3f;
struct Trip {
    ll umax, vmin;
    bool ok;
} tree[maxn << 2];
ll lazy[maxn << 2];
int n;
ll sumd[maxn], u[maxn], v[maxn], d[maxn];
inline int ls(int rt) {return rt << 1;}
inline int rs(int rt) {return rt << 1 | 1;}
void pushup(int rt) {
    tree[rt].umax = max(tree[ls(rt)].umax, tree[rs(rt)].umax);
    tree[rt].vmin = min(tree[ls(rt)].vmin, tree[rs(rt)].vmin);
    if (tree[ls(rt)].ok == true && tree[rs(rt)].ok == true && tree[ls(rt)].umax <= tree[rs(rt)].vmin)
        tree[rt].ok = true;
    else
        tree[rt].ok = false;
}
void pushdown(int rt) {
    tree[ls(rt)].umax += lazy[rt];
    tree[ls(rt)].vmin += lazy[rt];
    lazy[ls(rt)] += lazy[rt];
    tree[rs(rt)].umax += lazy[rt];
    tree[rs(rt)].vmin += lazy[rt];
    lazy[rs(rt)] += lazy[rt];
    lazy[rt] = 0;
}
void build(int lc, int rc, int rt) {
    lazy[rt] = 0;
    if (lc == rc) {
        tree[rt] = {u[lc], v[lc], true};
        return;
    }
    int mc = (lc + rc) >> 1;
    build(lc, mc, ls(rt));
    build(mc + 1, rc, rs(rt));
    pushup(rt);
}
void Pot_Add1(int x, ll y, int lc, int rc, int rt) {
    if (lc == rc) {
        tree[rt].umax += y;
        return;
    }
    pushdown(rt);
    int mc = (lc + rc) >> 1;
    if (x <= mc) Pot_Add1(x, y, lc, mc, ls(rt));
    else Pot_Add1(x, y, mc + 1, rc, rs(rt));
    pushup(rt);
}
void Pot_Add2(int x, ll y, int lc, int rc, int rt) {
    if (lc == rc) {
        tree[rt].vmin += y;
        return;
    }
    pushdown(rt);
    int mc = (lc + rc) >> 1;
    if (x <= mc) Pot_Add2(x, y, lc, mc, ls(rt));
    else Pot_Add2(x, y, mc + 1, rc, rs(rt));
    pushup(rt);
}
void Seg_Add(int L, int R, ll z, int lc, int rc, int rt) {
    if (R < lc || rc < L) return;
    if (L <= lc && rc <= R) {
        tree[rt].umax += z;
        tree[rt].vmin += z;
        lazy[rt] += z;
        return;
    }
    pushdown(rt);
    int mc = (lc + rc) >> 1;
    Seg_Add(L, R, z, lc, mc, ls(rt));
    Seg_Add(L, R, z, mc + 1, rc, rs(rt));
    pushup(rt);
}
Trip Query(int L, int R, int lc, int rc, int rt) {
    if (R < lc || rc < L) return {-inf, inf, true};
    if (L <= lc && rc <= R) {
        return tree[rt];
    }
    pushdown(rt);
    int mc = (lc + rc) >> 1;
    Trip res1 = Query(L, R, lc, mc, ls(rt));
    Trip res2 = Query(L, R, mc + 1, rc, rs(rt));
    pushup(rt);
    Trip res;
    if (res1.ok == true && res2.ok == true && res1.umax <= res2.vmin) res = {max(res1.umax, res2.umax), min(res1.vmin, res2.vmin), true};
    else res = {max(res1.umax, res2.umax), min(res1.vmin, res2.vmin), false};
    return res;
}
int main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int _, q;
    cin >> _;
    while (_--) {
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> u[i];
        for (int i = 1; i <= n; i++) cin >> v[i];
        for (int i = 1; i < n; i++) cin >> d[i];
        sumd[n] = 0;
        for (int i = n - 1; i >= 1; i--) sumd[i] = sumd[i + 1] + d[i];
        for (int i = 1; i <= n; i++) u[i] = u[i] + sumd[i], v[i] = v[i] + sumd[i];
        build(1, n, 1);
        cin >> q;
        int op;
        ll x, y, z;
        bool ok;
        while (q--) {
            cin >> op;
            if (op == 0) {
                cin >> x >> y;
                ok = Query(x, y, 1, n, 1).ok;
                if (ok) puts("Yes");
                else puts("No");
            }
            else if (op == 1) {
                cin >> x >> y;
                Seg_Add(1, x, y - d[x], 1, n, 1);
                d[x] = y;
            }
            else {
                cin >> x >> y >> z;
                Pot_Add1(x, y + sumd[x] - u[x], 1, n, 1);
                Pot_Add2(x, z + sumd[x] - v[x], 1, n, 1);
                u[x] = y + sumd[x], v[x] = z + sumd[x];
            }
        }
    }
}
/*
1
5
85 71 79 41 54
91 83 99 43 69
7 1 3 2
3
1 5 8
2 2 7 13
0 3 5

1
5
85 71 79 41 54
91 83 99 43 69
7 1 3 2
2
2 2 7 13
0 3 5

1
5
85 71 79 41 54
91 83 99 43 69
7 1 3 2
2
1 5 8
0 3 5

1
5
85 71 79 41 54
91 83 99 43 69
7 1 3 2
1
0 3 5
*/
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值