Description
今年 CEOI 的开幕式上有一场精彩的火炬表演。表演者们站成一排,从 1 开始从左往右编号。每个表演者举着一根火炬,初始只有一个举着点燃的火炬的表演者。
表演分为 Q 幕。在第 a 幕开始之前,要么 pa>0 个表演者从右侧加入表演,他们的火炬是熄灭的;要么最右侧 pa>0 个表演者决定离开,并熄灭他们的火炬(如果他们的火炬是点燃的)。表演者的加入和离开不受委员会的控制。最左侧的表演者永远留在台上。
一旦第 a 幕准备好了,委员会需要宣布一个非负整数 ta≥0。对于每个举着点燃的火炬的表演者,用他的火炬点燃他右侧 ta 个人的火炬。也就是说,第 i 个人的火炬在传火后是点燃的,当且仅当表演者 max(1,i−ta),⋯,i 中至少一个人的火炬在传火前是点燃的。ta 不应超过 pa。
在第 a 幕结束时,委员会需要告知每个举着点燃的火炬的表演者是否熄灭火炬。出于美学原因,最右侧的表演者应保持他的火炬点燃。此外,为了节省汽油,点燃的火炬数量不应超过 150。
编写程序帮助委员会在上述限制下主持表演。
对于所有数据,1≤N≤1017,1≤Q≤5×104。
Solution
考虑将整个 01 序列反转,转化为从开头加/删数。
设序列中第 i 个 1 的位置为 fi,当 p=fi 时就需要满足 fi+1≤fi+1≤2fi+1,同时 f1=1,ft=n。所以如果没有删除操作,就让 fi=min{2fi−1+1,n} 即可。
加入删除操作就不好做了,因为删掉开头的 p 个数后 f 数组的形态会变得很不固定,难以维护。
不妨设 g 数组为删除操作后的新的 1 的位置序列,考虑用原来的 f 序列构造 g 序列。
显然 gi−1+1≤gi≤2gi−1+1,由于 fj 控制的区间为 [fj−2p,fj−p],所以所有 gi 一定要出自这些区间。
假设已经构造好了 g1,g2,…,gi−1,设 j 为满足 fj−2p≤2gi−1+1 的最大的 j,那么 2fj+1−2p≥fj+1−2p>2gi−1+1,即 fj−p≥gi−1+1,所以 [fj−2p,fj−p] 与 [gi−1+1,2gi−1+1] 一定有交,贪心地令 gi 为 min{fj−p,2gi−1+1} 即可。
下面算一下这个做法维护的序列长度。
如果 gi=2gi−1+1,则 g 翻倍了。如果 gi=fj−p,则 2gi+1=2fj−2p+1≥fj+1−2p,所以 gi+1≥min{fj+1−p,2gi+1}。如果 gi+1=2gi+1,则 g 翻倍了,否则 gi+1=fj+1−p≥2gi−1+1。
所以 g 数组每两个就会翻一次倍,所以长度为 2logn+O(1)。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| #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}; }
|