[CEOI2023] A Light Inconvenience 題解

下蛋爷發表於2024-11-12

Description

今年 CEOI 的開幕式上有一場精彩的火炬表演。表演者們站成一排,從 \(1\) 開始從左往右編號。每個表演者舉著一根火炬,初始只有一個舉著點燃的火炬的表演者。

表演分為 \(Q\) 幕。在第 \(a\) 幕開始之前,要麼 \(p_a > 0\) 個表演者從右側加入表演,他們的火炬是熄滅的;要麼最右側 \(p_a > 0\) 個表演者決定離開,並熄滅他們的火炬(如果他們的火炬是點燃的)。表演者的加入和離開不受委員會的控制。最左側的表演者永遠留在臺上。

一旦第 \(a\) 幕準備好了,委員會需要宣佈一個非負整數 \(t_a \geq 0\)。對於每個舉著點燃的火炬的表演者,用他的火炬點燃他右側 \(t_a\) 個人的火炬。也就是說,第 \(i\) 個人的火炬在傳火後是點燃的,當且僅當表演者 \(\max(1, i - t_a), \cdots, i\) 中至少一個人的火炬在傳火前是點燃的。\(t_a\) 不應超過 \(p_a\)

在第 \(a\) 幕結束時,委員會需要告知每個舉著點燃的火炬的表演者是否熄滅火炬。出於美學原因,最右側的表演者應保持他的火炬點燃。此外,為了節省汽油,點燃的火炬數量不應超過 \(150\)

編寫程式幫助委員會在上述限制下主持表演。

對於所有資料,\(1\leq N\leq 10 ^ {17}\)\(1\leq Q\leq 5\times 10 ^ 4\)

Solution

考慮將整個 01 序列反轉,轉化為從開頭加/刪數。

設序列中第 \(i\)\(1\) 的位置為 \(f_i\),當 \(p=f_i\) 時就需要滿足 \(f_i+1\leq f_{i+1}\leq 2f_i+1\),同時 \(f_1=1,f_t=n\)。所以如果沒有刪除操作,就讓 \(f_i=\min\{2f_{i-1}+1,n\}\) 即可。

加入刪除操作就不好做了,因為刪掉開頭的 \(p\) 個數後 \(f\) 陣列的形態會變得很不固定,難以維護。

不妨設 \(g\) 陣列為刪除操作後的新的 \(1\) 的位置序列,考慮用原來的 \(f\) 序列構造 \(g\) 序列。

顯然 \(g_{i-1}+1\leq g_i\leq 2g_{i-1}+1\),由於 \(f_j\) 控制的區間為 \([f_j-2p,f_j-p]\),所以所有 \(g_i\) 一定要出自這些區間。

假設已經構造好了 \(g_1,g_2,\ldots,g_{i-1}\),設 \(j\) 為滿足 \(f_j-2p\leq 2g_{i-1}+1\) 的最大的 \(j\),那麼 \(2f_j+1-2p\geq f_{j+1}-2p>2g_{i-1}+1\),即 \(f_j-p\geq g_{i-1}+1\),所以 \([f_j-2p,f_j-p]\)\([g_{i-1}+1,2g_{i-1}+1]\) 一定有交,貪心地令 \(g_i\)\(\min\{f_j-p,2g_{i-1}+1\}\) 即可。

下面算一下這個做法維護的序列長度。

如果 \(g_i=2g_{i-1}+1\),則 \(g\) 翻倍了。如果 \(g_i=f_j-p\),則 \(2g_i+1=2f_j-2p+1\geq f_{j+1}-2p\),所以 \(g_{i+1}\geq\min\{f_{j+1}-p,2g_i+1\}\)。如果 \(g_{i+1}=2g_i+1\),則 \(g\) 翻倍了,否則 \(g_{i+1}=f_{j+1}-p\geq 2g_{i-1}+1\)

所以 \(g\) 陣列每兩個就會翻一次倍,所以長度為 \(2\log n+O(1)\)

Code

#include <bits/stdc++.h>
#include "light.h"

#ifdef ORZXKR
#include "sample_grader.cpp"
#endif

const int kMaxt = 155;

int64_t n = 1, t = 0;
int64_t f[kMaxt], g[kMaxt];

void prepare() {
  n = 1;
  f[t = 1] = 1;
}

std::pair<long long, std::vector<long long>> join(long long p) {
  if (f[t] == n) --t;
  if (!t) f[t = 1] = 1;
  n += p;
  for (;;) {
    if (f[t] == n) break;
    f[t + 1] = std::min<int64_t>(2ll * f[t] + 1, n);
    ++t;
  }
  std::vector<long long> vec;
  for (int i = t; i; --i) vec.emplace_back(n + 1 - f[i]);
  return {p, vec};
}

std::pair<long long, std::vector<long long>> leave(long long p) {
  n -= p;
  int _t = 0;
  g[_t = 1] = 1;
  for (int i = 1;;) {
    if (g[_t] == n) break;
    for (; i < t && f[i + 1] - 2ll * p <= 2ll * g[_t] + 1; ++i) {}
    g[_t + 1] = std::min<int64_t>({n, f[i] - p, 2ll * g[_t] + 1});
    ++_t;
  }
  t = _t;
  for (int i = 1; i <= t; ++i) f[i] = g[i];
  std::vector<long long> vec;
  for (int i = t; i; --i) vec.emplace_back(n + 1 - f[i]);
  return {p, vec};
}

相關文章