CF1286E Fedya the Potter Strikes Back 题解
jiangly
2020-01-10 19:07:21
#### 题意
你有一个序列,序列的每一个位置有一个字母 $c_i$ 和一个数 $w_i$。一个区间的权值定义为区间内 $w_i$ 的最小值。
初始时序列为空,你要执行 $n$ 次操作,每次在序列末尾添加一个元素(字母和数),并求出操作后序列的每一个满足下列条件的区间的权值和:
- 该区间和与其长度相等的前缀形成的字符串相同。
强制在线。
#### 限制
$1\le n\le 600000$, $|\Sigma|=26$。
#### 题解
考虑在每次加入一个字符后,求出所有合法后缀(即 border)的权值和。容易想到用 KMP 算法解决。
具体的,我们维护 border 的集合。加入一个字符 $c_i$ 后,对集合的改变为:
- 如果一个 border 对应前缀的下一个字符不是 $c$,将其删除。
- 如果 $c_i=c_0$,加入长度为 $1$ 的 border。
由于最多加入 $n$ 个 border,所以每次操作删除的 border 个数是均摊 $O(1)$ 的。
我们考虑如何快速找到被删除的 border。由于这些 border 都在 KMP 算法中从 $i$ 到 $0$ 的路径上(即 $i$, $\mathrm{next}_i$, $\mathrm{next}_{\mathrm{next}_i},\ldots,0$),我们对每一个节点记录其第一个后继字符不一样的祖先,这样我们就能在 $O(1)$ 时间内找到下一个被删除的 border。根据上面的分析,这样做的复杂度是均摊 $O(1)$ 的。
最后是权值的处理。在删除一个 border 时,需要询问它的权值,可以通过单调栈 + 二分解决。还需要把所有大于 $w_i$ 的权值修改为 $w_i$,我们使用 `std::map` 维护每个权值以及出现次数,修改时暴力找到所有大于 $w_i$ 的权值,这样做的复杂度是均摊 $O(\log n)$。
时间复杂度:$O(n\log n)$。
#### 代码
```cpp
#include <iostream>
#include <vector>
#include <string>
#include <iomanip>
#include <map>
#include <algorithm>
constexpr long long P = 1'000'000'000'000'000'000;
long long ansH, ansL;
int ans26, ansB;
int n;
std::string s;
char c;
std::vector<int> anc, nxt, w, stk{0};
std::map<int, int> cnt;
void print() {
if (ansH == 0) {
std::cout << ansL << "\n";
return;
}
std::cout << ansH << std::setw(18) << std::setfill('0') << ansL << "\n";
}
int query(int pos) {
return w[*std::lower_bound(stk.begin(), stk.end(), pos)];
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n;
anc.resize(n + 1);
nxt.resize(n + 1);
w.resize(n);
std::cin >> c >> w[0];
s += c;
ans26 = w[0] % 26;
ansL = ansB = w[0];
print();
long long sum = 0;
for (int i = 1, j = 0; i < n; ++i) {
std::cin >> c >> w[i];
c = (c - 'a' + ans26) % 26 + 'a';
s += c;
w[i] ^= ansB;
while (j > 0 && c != s[j])
j = nxt[j];
if (c == s[j])
++j;
nxt[i + 1] = j;
if (c == s[nxt[i]]) {
anc[i] = anc[nxt[i]];
} else {
anc[i] = nxt[i];
}
for (int k = i; k > 0; ) {
if (s[k] == c) {
k = anc[k];
} else {
int v = query(i - k);
--cnt[v];
sum -= v;
if (cnt[v] == 0)
cnt.erase(v);
k = nxt[k];
}
}
if (s[0] == c) {
++cnt[w[i]];
sum += w[i];
}
while (!stk.empty() && w[i] <= w[stk.back()])
stk.pop_back();
stk.push_back(i);
int nw = 0;
for (auto it = cnt.upper_bound(w[i]); it != cnt.end(); ) {
sum -= 1LL * (it -> first - w[i]) * it -> second;
nw += it -> second;
auto j = std::next(it);
cnt.erase(it);
it = j;
}
cnt[w[i]] += nw;
long long res = w[stk[0]] + sum;
ansL += res;
if (ansL >= P) {
ansL -= P;
++ansH;
}
ans26 = (ans26 + res) % 26;
ansB = (ansB + res) & ((1 << 30) - 1);
print();
}
return 0;
}
```