1 条题解

  • 0
    @ 2025-8-24 23:05:34

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar win114514
    过去的我正在消散,未来的我模糊不清,唯一能相信的只有现在的自己。

    搬运于2025-08-24 23:05:34,当前版本为作者最后更新于2024-11-05 16:58:31,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    很有意思的一道题。

    思路

    首先将相邻一样的数合并,每个元素变成一个二元组,表示数与出现次数。

    考虑什么时候不能合并。

    我们发现假如充分合并后,现在有连续的三个数 x1,x2,x3x_1,x_2,x_3,以及他们各自的出现次数 y1,y2,y3y_1,y_2,y_3

    如果 x1>x2,x3>x2x_1>x_2,x_3>x_2

    我们想要合并这三个,必须要先把 x2x_2 合并成 min(x1,x3)\min(x_1,x_3)

    但是如果做不到的话,那么这三个数就一定不可能合并了。

    相当于整个序列从这里断开了。

    这样的话,整个序列是一堆单峰序列。

    考虑末尾追加一个二元组 (x,y)(x,y) 会有什么影响:

    1. 序列尾部元素与 xx 相等。

      累加 yy 即可。

    2. 序列元素少于两个。

      直接插入即可。

    3. 序列尾部大于 xx, 或尾部大于倒数第二个。

      同样直接插入即可。

    4. 除了以上情况,我们就需要一些处理了。

      将末尾与倒数第二个提出来,记为 (x1,y1),(x2,y2)(x_1,y_1),(x_2,y_2)

      首先我们判断能否将 x1x_1 提升至 min(x,x2)\min(x,x_2),如果可以,我们就先将其提升,然后再递归插入。

      如果不可以,我们可以插入一个极大值作为分割符,然后将 x1x_1 的贡献往 xxx2x_2 上累加,再递归插入。

    线段树维护即可。

    时间复杂度:O(nvlogn)O(nv\log n)

    Code

    #include <bits/stdc++.h>
    using namespace std;
    
    int n;
    int a[100010];
    struct Node {
      int ns = 0;
      vector<pair<int, int>> cr{};
      inline void init(int x) { vector<pair<int, int>>{{x, 1}}.swap(cr); ns = x; }
      inline void add(pair<int, int> x) {
        if (x.second) ns = max(ns, x.first + __lg(x.second));
        if (cr.size() && cr.back().first == x.first) {
          cr.back().second += x.second;
          if (cr.back().second)
            ns = max(ns, cr.back().first + __lg(cr.back().second));
        }
        else if (cr.size() <= 1)
          cr.push_back(x);
        else if (cr.back().first > x.first)
          cr.push_back(x);
        else if (cr.size() >= 2 && cr.back().first > cr[cr.size() - 2].first)
          cr.push_back(x);
        else {
          auto y = cr.back();
          cr.pop_back();
          auto z = cr.back();
          int d = min(30, min(x.first, z.first) - y.first);
          if ((y.second & (-y.second)) >= (1 << d))
            add({y.first + d, y.second >> d}), add(x);
          else {
            if (__lg(y.second) >= z.first - y.first)
              add({z.first, y.second >> (z.first - y.first)});
            add({1e9, 0});
            if (__lg(y.second) >= x.first - y.first)
              x.second += y.second >> (x.first - y.first);
            add(x);
          }
        }
      }
      inline friend Node operator+(Node a, Node b) {
        Node c = a;
        c.ns = max(b.ns, c.ns);
        for (auto i : b.cr) c.add(i);
        return c;
      }
    } ns, d[200010];
    
    inline void build(int p, int l, int r) {
      if (l == r) d[p].init(a[l]);
      else {
        int mid = (l + r) >> 1;
        build(mid<<1, l, mid);
        build(mid<<1|1, mid + 1, r);
        d[p] = d[mid<<1] + d[mid<<1|1];
      }
    }
    inline void upd(int p, int l, int r, int k) {
      if (l == r) d[p].init(a[l]);
      else {
        int mid = (l + r) >> 1;
        if (mid >= k) upd(mid<<1, l, mid, k); else upd(mid<<1|1, mid + 1, r, k);
        d[p] = d[mid<<1] + d[mid<<1|1];
      }
    }
    inline void ask(int p, int l, int r, int L, int R) {
      if (L <= l && r <= R) ns = ns + d[p];
      else {
        int mid = (l + r) >> 1;
        if (L <= mid) ask(mid<<1, l, mid, L, R);
        if (mid <  R) ask(mid<<1|1, mid + 1, r, L, R);
      }
    }
    void prepare_game(vector<int> A) {
      n = A.size();
      for (int i = 1; i <= n; i++) a[i] = A[i - 1];
      build(1, 1, n);
    }
    int play_game(int l, int r) {
      ns.cr.clear();
      ns.ns = 0;
      ns.add({1e9, 0});
      ask(1, 1, n, l + 1, r + 1);
      ns.add({1e9, 0});
      return ns.ns;
    }
    void update_game(int p, int v) {
      a[p + 1] = v;
      upd(1, 1, n, p + 1);
    }
    
    • 1

    信息

    ID
    10935
    时间
    4000ms
    内存
    1000MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者