P11236 「KTSC 2024 R1」水果游戏 题解

很有意思的一道题。

思路#

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

考虑什么时候不能合并。

我们发现假如充分合并后,现在有连续的三个数 x1,x2,x3,以及他们各自的出现次数 y1,y2,y3

如果 x1>x2,x3>x2

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

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

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

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

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

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

    累加 y 即可。

  2. 序列元素少于两个。

    直接插入即可。

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

    同样直接插入即可。

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

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

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

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

线段树维护即可。

时间复杂度:O(nvlogn)

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);
}

作者:JiaY19

出处:https://www.cnblogs.com/JiaY19/p/18528332

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   JiaY19  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示