[北大集訓 2021] 簡單資料結構

lalaouye發表於2024-10-04

簡單資料結構,但本蒟蒻覺得並不簡單吶!

容易發現這題的幾個好用的性質:

1.只要被第一個操作影響的都能夠保持單調,容易一起維護。

2.操作都是全域性的!

3.沒被操作一影響的都可以表示為 \(ki+a_i\) 的形式。

利用這些性質,我們考慮把沒被操作一影響的項放在 \(S\) 集合,被操作一影響的項放在 \(T\) 集合。現在我們考慮動態維護這兩個東西,事實上我們可以先知道每一項是在什麼時候從 \(S\) 進入 \(T\) 的(顯然初始每一項都在 \(S\))。

每次我們進行操作一時,都會把所有滿足 \(a_i\ge v-ki\) 的所有項併入 \(T\),這種一次函式的形式啟發我們維護一個凸包,上面每個點為 \((i,a_i)\),然後維護一個上凸包,但是凸包太大了,不好修改,怎麼辦?我們發現刪點與重構凸包複雜度非常不均,所以考慮分塊,我們這樣每次刪掉一個點,再 \(\mathcal{O}(\sqrt n)\) 重構即可。因為只會刪掉 \(n\) 個點,所以複雜度是對的,但是這裡有個小問題,就是我們單次操作找每個塊內凸包的點有可能退化到 \(\mathcal{O}(n)\),沒關係,我們的 \(k\) 是單調的,每個塊內維護個雙指標即可。

對答案的貢獻用樹狀陣列可以簡單維護。

然後考慮維護 \(T\),首先對於 \(1\) 操作相當於是一段字尾推平,線段樹上二分隨便做。對於操作二的貢獻線上段樹上維護增加次數 \(delta\),並且維護可提供貢獻的點 \(sum\),因為線段樹上可以產生貢獻的點並不連續。對於最大值的維護也是簡單的,知道區間中最右邊的在 \(T\) 中的點就知道最大值了。

時間複雜度 \(\mathcal{O}(n\sqrt n)\),這裡 \(n,q\) 同級。

程式碼:

#include <bits/stdc++.h>
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; ++ i)
#define rrp(i, l, r) for (int i = r; i >= l; -- i)
#define pii pair <int, int>
#define eb emplace_back
#define inf 1000000000
#define id(x, y) n * (x - 1) + y
#define ls p << 1
#define rs ls | 1
using namespace std;
constexpr int N = 2e5 + 5, M = (1ll << 31) - 1, P = 998244353;
constexpr double PI = acos (-1.0);
inline int rd () {
  int x = 0, f = 1;
  char ch = getchar ();
  while (! isdigit (ch)) {
    if (ch == '-') f = -1;
    ch = getchar ();
  }
  while (isdigit (ch)) {
    x = (x << 1) + (x << 3) + ch - 48;
    ch = getchar ();
  }
  return x * f;
}
int qpow (int x, int y) {
  int ret = 1;
  for (; y; y >>= 1, x = x * x % P) if (y & 1) ret = ret * x % P;
  return ret;
}

namespace seg {
  class node {
    public:
      int sum, val, mx, R, cnt, tag1, tag2;
  } t[N << 2];
  void clear () {
    rep (i, 1, N * 4 - 1) {
      t[i].sum = t[i].val = 0;
      t[i].mx = t[i].R = t[i].cnt = 0;
      t[i].tag1 = - 1; t[i].tag2 = 0;
    }
  }
  void merge (node &ret, node x, node y) {
    ret.sum = x.sum + y.sum;
    ret.val = x.val + y.val;
    ret.mx = max (x.mx, y.mx);
    ret.R = max (x.R, y.R);
    ret.cnt = x.cnt + y.cnt;
  }
  void cov (int p, int val) {
    t[p].val = t[p].cnt * val;
    t[p].mx = val;
    t[p].tag1 = val;
    t[p].tag2 = 0;
  }
  void add (int p, int x) {
    t[p].val += t[p].sum * x;
    t[p].mx += t[p].R * x; 
    t[p].tag2 += x;
  }
  void psd (int p) {
    if (t[p].tag1 > -1) {
      cov (ls, t[p].tag1);
      cov (rs, t[p].tag1);
      t[p].tag1 = -1;
    }
    if (t[p].tag2) {
      add (ls, t[p].tag2);
      add (rs, t[p].tag2);
      t[p].tag2 = 0;
    }
  }
  int find (int p, int l, int r, int x) {
    if (l == r) {
      if (t[p].val >= x) return l; else return 0;
    }
    int mid = l + r >> 1;
    psd (p);
    
    if (t[ls].mx >= x) return find (ls, l, mid, x);
    else return find (rs, mid + 1, r, x);
  }
  void modify (int p, int l, int r, int L, int R, int v) {
    if (L <= l && r <= R) return cov (p, v), void ();
    int mid = l + r >> 1; psd (p);
    if (L <= mid) modify (ls, l, mid, L, R, v);
    if (R > mid) modify (rs, mid + 1, r, L, R, v);
    merge (t[p], t[ls], t[rs]);
  }
  void upd (int p, int l, int r, int x, int k) {
    if (l == r) {
      t[p].sum = x, t[p].cnt = 1;
      t[p].R = x, t[p].val = t[p].mx = k;
      return ;
    } psd (p); int mid = l + r >> 1;
    if (x <= mid) upd (ls, l, mid, x, k);
    else upd (rs, mid + 1, r, x, k);
    merge (t[p], t[ls], t[rs]);
  }
  int qry (int p, int l, int r, int L, int R) {
    if (L <= l && r <= R) return t[p].val;
    int mid = l + r >> 1, ret = 0; psd (p);
    if (L <= mid) ret += qry (ls, l, mid, L, R);
    if (R > mid) ret += qry (rs, mid + 1, r, L, R);
    return ret;
  }
}

int n, m, q;
int a[N];
struct FWT {
  int c[N];
  int lb (int x) { return x & -x; }
  void upd (int x, int y) {
    for (; x <= n; c[x] += y, x += lb (x)) ;
  }
  int qry (int x) {
    int ret = 0;
    for (; x; ret += c[x], x -= lb (x)) ;
    return ret;
  }
} s, t;
class node {
  public:
    int opt;
    int l, r;
} g[N];

class Dot {
  public:
    int x, y;
  friend Dot operator + (const Dot &a, const Dot &b) {
    return (Dot) {a.x + b.x, a.y + b.y};
  }
  friend Dot operator - (const Dot &a, const Dot &b) {
    return (Dot) {a.x - b.x, a.y - b.y};
  }
  friend int operator * (const Dot &a, const Dot &b) {
    return a.x * b.y - a.y * b.x;
  }
} d[N];
vector <int> vec[N];
class block {
  public:
  int l, r, ld, rd;
  int stk[505];
  void build () {
    l = 1, r = 0;
    rep (i, ld, rd) {
      if (d[i].y < 0) continue;
      while (l < r && (d[stk[r]] - d[stk[r - 1]]) * (d[i] - d[stk[r]]) >= 0) -- r;
      stk[++ r] = i;
    }
  }
  void del (int k, int id, int x) {
    while (l <= r) {
      while (l < r && d[stk[l]].x * k + d[stk[l]].y <= d[stk[l + 1]].x * k + d[stk[l + 1]].y) ++ l;
      if (d[stk[l]].x * k + d[stk[l]].y >= x) vec[id].eb (d[stk[l]].x), d[stk[l]].y = -1; else return ;
      build ();
    }
  }
} bl[505];
void init () {
  int B = sqrt (n);
  m = n / B + (bool) (n % B);
  rep (i, 1, m) {
    bl[i].ld = bl[i - 1].rd + 1;
    bl[i].rd = min (bl[i - 1].rd + B, n);
    bl[i].build ();
  }
}
signed main () {
  // freopen ("1.in", "r", stdin);
  // freopen ("1.out", "w", stdout);
  n = rd (), q = rd ();
  rep (i, 1, n) a[i] = rd (), d[i] = (Dot) {i, a[i]};
  init ();
  int now = 0;
  rep (i, 1, q) {
    g[i].opt = rd ();
    if (g[i].opt == 1) {
      g[i].l = rd ();
      rep (j, 1, m) bl[j].del (now, i, g[i].l);
    } else if (g[i].opt == 2) {
      ++ now;
    } else g[i].l = rd (), g[i].r = rd ();
  }
  rep (i, 1, n) {
    s.upd (i, i), t.upd (i, a[i]);
  }
  seg::clear ();
  now = 0;
  rep (i, 1, q) {
    if (g[i].opt == 1) {
      int pos = seg::find (1, 1, n, g[i].l);
      if (pos) seg::modify (1, 1, n, pos, n, g[i].l);
      for (auto p : vec[i]) {
        s.upd (p, - p), t.upd (p, - a[p]);
        seg::upd (1, 1, n, p, g[i].l);
      }
    } else {
      if (g[i].opt == 2) {
        ++ now; seg::add (1, 1);
      } else {
        int l = g[i].l, r = g[i].r;
        printf ("%lld\n", now * (s.qry (r) - s.qry (l - 1)) + t.qry (r) - t.qry (l - 1) + seg::qry (1, 1, n, l, r));
      }
    }
  }
}

相關文章