P13508 [OOI 2024] Burenka and Pether

对于任意一个点 \(i\)\(i\) 能直接到达的点 \(p\) 需要 \(a_p\ge a_i\),且 \(p\le r_i\),其中 \(r_i\)\(i\) 能到的最后一个 \(<a_i\) 的位置 \(+d\)\(r_i\) 可以按值域扫描线预处理。

对于 \(a_{v_i}\) 扫描线,同时禁用所有 \(>a_{v_i}\) 的元素。那么此时对于一个点 \(i\),它的下一步就一定是走到 \([i,r_i]\) 中未禁用的最大值的位置,不妨设为 \(p_i\)

考虑在扫描线同时维护出 \(p_i\)。扫描的值 \(v\to v+1\) 时,所有 \([i,r_i]\) 包含 \(v+1\) 对应下标的 \(i\)\(p_i\) 都会更新,总修改数是非常大的。一个根号分治的思路是我们只修改 \(r_i-i+1\le\sqrt{n}\) 的所有 \(p_i\),这样修改数就降到了 \(O(n\sqrt{n})\)

对于查询,考虑用类似弹飞绵羊的做法处理 \(r_i-i+1\le\sqrt{n}\),虽然修改次数 \(O(n\sqrt{n})\),但是都集中在左侧的 \(O(1)\) 个块,只要对于这些块重构即可。如果当前区间长度 \(>\sqrt{n}\) 就查询一次区间 \(\max\),这个可以用分块维护做到改 \(O(\sqrt{n})\) 改,\(O(\frac{len}{\sqrt{n}})\) 查。

如果对 \([i,r_i]\) 进行了一次区间查 \(\max\),那么下一次往后一定会跳到 \(>r_i\) 的位置上。因此查询 \(\max\) 的区间长度和是 \(O(n)\) 的,总复杂度就是 \(O((n+q)\sqrt{n})\)

#include <bits/stdc++.h>
using namespace std;

const int kN = 3e5 + 5, B = 550, kC = kN / B + 5;
int n, d, g, q;
int a[kN], inv[kN], lim[kN], nxt[kN];
vector<int> qrv[kN];

struct Qry {
  int op, l, r;
} qry[kN];
int res[kN];

void PreWork() {
  set<int> st {0, n + 1};
  set<pair<int, int>> sp {make_pair(0, n + 1)};
  for(int i = 1; i <= n; i++) {
    int id = inv[i];
    auto it = st.lower_bound(id);
    auto pv = prev(it);
    if(*it - *pv > d) sp.erase(make_pair(*pv, *it));
    if(*it - id > d) sp.emplace(id, *it);
    if(id - *pv > d) sp.emplace(*pv, id);
    st.insert(it, id);
    auto itt = sp.lower_bound(make_pair(id, 0));
    if(itt != sp.begin() && (prev(itt) -> second > id + d)) {
      lim[id] = id + d;
    }else if(itt != sp.end()) {
      lim[id] = itt -> first + d;
    }else {
      lim[id] = n;
    }
  }
}

int Bel(int x) { return (x - 1) / B + 1; }
int L(int b) { return (b - 1) * B + 1; }
int R(int b) { return min(b * B, n); }
struct DS1 {
  int pre[kN], suf[kN];
  int mx[kC];
  void Modify(int x, int v) {
    int b = Bel(x), bl = L(b), br = R(b);
    mx[b] = v;
    fill(pre + x, pre + br + 1, v);
    fill(suf + bl, suf + x + 1, v);
  }
  int Query(int l, int r) {
    assert(r - l + 1 > B);
    int bl = Bel(l), br = Bel(r);
    int res = max(suf[l], pre[r]);
    for(int i = bl + 1; i < br; i++) res = max(res, mx[i]);
    return res;
  }
} ds1;

struct DS2 {
  int nxt[kN], jp[kN], dep[kN];
  void Init() {
    for(int i = 1; i <= n; i++) {
      jp[i] = i;
    }
  }
  void Modify(int x, int v) { nxt[x] = v; }
  void ReBuild(int b) {
    int l = L(b), r = R(b);
    for(int i = r; i >= l; i--) {
      if(!nxt[i] || (nxt[i] > r)) jp[i] = i, dep[i] = 0;
      else {
        jp[i] = jp[nxt[i]];
        dep[i] = dep[nxt[i]] + 1;
      }
    }
  }
} ds2;

int main() {
  // freopen("root.in", "r", stdin);
  // freopen("root.out", "w", stdout);
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> d >> g;
  for(int i = 1; i <= n; i++) {
    cin >> a[i];
    inv[a[i]] = i;
  }
  cin >> q;
  for(int i = 1; i <= q; i++) {
    cin >> qry[i].op >> qry[i].l >> qry[i].r;
    if(a[qry[i].l] < a[qry[i].r]) {
      qrv[qry[i].r].push_back(i);
    }
  }
  PreWork();
  for(int i = 1; i <= n; i++) lim[i] = min(n, lim[i]);
  ds2.Init();
  for(int v = 1; v <= n; v++) {
    int p = inv[v];
    ds1.Modify(p, v);
    int mn = n + 1, mx = 0;
    for(int i = p - 1; i >= max(1, p - B + 1); i--) {
      if((lim[i] - i + 1 <= B) && (lim[i] >= p)) {
        mn = min(mn, i);
        mx = max(mx, i);
        ds2.Modify(i, nxt[i] = p);
      }
    }
    if(mn <= mx) {
      int bl = Bel(mn), br = Bel(mx);
      for(int i = bl; i <= br; i++) {
        ds2.ReBuild(i);
      }
    }
    for(int id : qrv[p]) {
      int l = qry[id].l, ans = 0;
      while(l < p) {
        while(Bel(l) < Bel(p)) {
          ans += ds2.dep[l];
          l = ds2.jp[l];
          if(!nxt[l]) break;
          if(nxt[l] < p) l = nxt[l], ans++;
          else break;
        }
        while(nxt[l] && (nxt[l] < p)) l = nxt[l], ans++;
        if(!nxt[l] && (lim[l] - l + 1 <= B)) { ans = 0; break; }
        if(l == p) break;
        if(nxt[l] == p) { ans++; break; }
        if(!nxt[l]) {
          int old = l;
          l = inv[ds1.Query(l, lim[l])];
          if(l == old) { ans = 0; break; }
          else ans++;
        }
      }
      res[id] = ((qry[id].op == 1) ? (ans >= 1) : ans);
    }
  }
  for(int i = 1; i <= q; i++) {
    cout << res[i] << "\n";
  }
  return 0;
}
posted @ 2025-11-12 15:15  CJzdc  阅读(21)  评论(0)    收藏  举报