线段树 ---- 2021牛客多校第一场 J Journey among Railway Stations [线段树维护区间可行性判断]

题目链接


题目大意:

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

N , q ∈ [ 1 , 1 e 6 ] N,q\in[1,1e6] N,q[1,1e6]


解题思路:

  1. [ l , l + 1 , l + 2.... + r − 1 , r ] [l,l+1,l+2....+r-1,r] [l,l+1,l+2....+r1,r]实际上我们可以这么来看,我们先把 [ u l , v l ] + w l → [ u l + w l , v l + w l ] [u_l,v_l]+w_l\rightarrow[u_l+w_l,v_l+w_l] [ul,vl]+wl[ul+wl,vl+wl]然后和 [ u l + 1 , v l + 1 ] [u_{l+1},v_{l+1}] [ul+1,vl+1]求区间交

  2. 但是不是简单的区间交,因为是可以的等待的,那么就是左端点取 m a x max max,但是右端点还是取 v l + 1 v_{l+1} vl+1

  3. 区间就变成了 [ m a x ( u l + w l , u l + 1 ) , v l + 1 ] [max(u_l+w_l,u_{l+1}),v_{l+1}] [max(ul+wl,ul+1),vl+1]然后再把这个区间加 w l + 1 w_{l+1} wl+1去迭代(假设相交部分是绿色的)
    在这里插入图片描述

  4. 但是我们不可能每次都把绿色拿出来那么我们可以直接把 L L L区间和 L + 1 L+1 L+1的区间直接拿去和 L + 2 L+2 L+2区间相交,现在就是蓝色的部分啦,就是三个区间交!

在这里插入图片描述

  1. 我们发现就是对于把 L L L L + 1 L+1 L+1区间 a d d ( w l + 1 ) add(w_{l+1}) add(wl+1) L + 2 L+2 L+2交对于 L L L是加上 w l + w l + 1 w_l+w_{l+1} wl+wl+1
  2. 实际上就是加上 w i w_i wi的后缀和就可以了 [ u L , v L ] + w l + . . . . w n − 1 [u_L,v_L]+w_l+....w_{n-1} [uL,vL]+wl+....wn1
  3. 那么所以的 [ 1 , n ] 的 时 间 段 就 变 成 了 [ u i + w i + . . . + w n − 1 , v i + w i + . . . + w n − 1 ] [1,n]的时间段就变成了[u_i+w_i+...+w_{n-1},v_i+w_i+...+w_{n-1}] [1,n][ui+wi+...+wn1,vi+wi+...+wn1]
  4. 那么是不是判断这 [ l , r ] [l,r] [l,r]里面区间是否有没有交集呢?
  5. 不是!!假设我们处理后的区间长 [ l , l + 4 ] [l,l+4] [l,l+4]长这样
  6. 对于任意的标号小于自己的区间只能小于自己,就是 u j ≤ v i [ j ≤ i ] u_j\leq v_i[j\leq i] ujvi[ji]!! 因为可以等待!!
  7. 那就是线段树了!!
  8. 叶子节点维护区间左端点的最大值 l m a x lmax lmax和右端点最小值 r m i n rmin rmin
  9. 合并的时候判断左区间的左端点的最大值和右区间右端点的最小值就可以了
  10. 一个ok表示下面的区间是否合法(就是是否可以在任意站停靠)
  11. 区间修改 w i w_i wi,因为修改一个 w i w_i wi影响了线段树里面 [ 1 , i ] [1,i] [1,i]里面所以点的权值和单点修改 [ u l , v l ] [u_l,v_l] [ul,vl]只对特定点修改了

在这里插入图片描述


AC code

O ( n l o g n ) O(nlogn) O(nlogn)

#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define endl '\n'
using namespace std;
const int maxn = 1000010;
typedef long long ll;
typedef pair<ll,ll> PLL;
int n;
struct inf {ll lmax, rmin;bool ok;};
PLL seg[maxn];
ll w[maxn], pre[maxn];
struct node {
   ll lmax[maxn << 2];
   ll rmin[maxn << 2];
   ll tag[maxn << 2];
   bool ok[maxn << 2];
   inline void pushup(int rt) {
      lmax[rt] = max(lmax[rt<<1],lmax[rt<<1|1]);
      rmin[rt] = min(rmin[rt<<1],rmin[rt<<1|1]);
      if((lmax[rt<<1] > rmin[rt<<1|1]) || ok[rt<<1] || ok[rt<<1|1]) ok[rt] = 1;// 合并判断
      else ok[rt] = 0;
   }
   inline void pushdown(int rt) {
      tag[rt<<1] += tag[rt];
      tag[rt<<1|1] += tag[rt];
      lmax[rt<<1] += tag[rt];
      lmax[rt<<1|1] += tag[rt];
      rmin[rt<<1] += tag[rt];
      rmin[rt<<1|1] += tag[rt];
      tag[rt] = 0;
   }
   void build(int rt, int l, int r) {
      tag[rt] = 0;
      ok[rt] = 0;
      if(l == r) {
         lmax[rt] = pre[l] + seg[l].first;
         rmin[rt] = pre[l] + seg[l].second;
         return;
      }
      build(Lson);
      build(Rson);
      pushup(rt);
   }
   void update(int rt ,int l, int r, int posl, int posr, int val, int ok) {
      if(posl <= l && posr >= r) {
         tag[rt] += val;
         if(ok==0) lmax[rt] += val;// 单点修改判断是改哪个?
         else if(ok==1)rmin[rt] += val;
         else lmax[rt] += val, rmin[rt] += val;
         return;
      }
      pushdown(rt);
      if(posl <= mid) update(Lson,posl,posr,val,ok);
      if(posr > mid) update(Rson,posl,posr,val,ok);
      pushup(rt);
   }
   inf ask(int rt, int l, int r, int posl, int posr) {
      if(posl <= l && posr >= r) return {lmax[rt],rmin[rt],ok[rt]};
      pushdown(rt);
      inf resl = {(ll)-1e18,(ll)1e18,0};
      inf resr = {(ll)-1e18,(ll)1e18,0};
      if(posl <= mid) resl = ask(Lson,posl,posr);
      if(posr > mid) {
         resr = ask(Rson,posl,posr);
         resr.ok |= ((resl.lmax > resr.rmin) | resl.ok); // 合并的时候判断
         resr.lmax = max(resl.lmax,resr.lmax);
         resr.rmin = min(resl.rmin,resr.rmin);
         return resr;
      }      
      return resl;
   } 
}sgt;
int main() {
    IOS;
    int _;
    cin >> _;
    while(_--) {
      cin >> n; 
      for(int i = 1; i <= n; ++ i) cin >> seg[i].first;
      for(int i = 1; i <= n; ++ i) cin >> seg[i].second;
      for(int i = 1; i < n; ++ i) cin >> w[i];
      pre[n] = 0;
      for(int i = n-1; i >= 1; -- i) pre[i] = w[i] + pre[i+1];//求个后缀和
      sgt.build(1,1,n);
      int q;
      cin >> q;
      while(q--) {
         int op, l, r, val;
         cin >> op >> l >> r;
         if(op == 0) {
            inf res = sgt.ask(1,1,n,l,r);
            if(res.ok) cout << "No\n";
            else cout << "Yes\n";
         } else if(op == 1) {
            sgt.update(1,1,n,1,l,r-w[l],2);
            w[l] = r;// 注意更新
         } else {
            cin >> val;
            sgt.update(1,1,n,l,l,r-seg[l].first,0);
            sgt.update(1,1,n,l,l,val-seg[l].second,1);
            seg[l].first = r;
            seg[l].second = val;// 注意更新
         }
      }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值