很牛的题。
定义一个 border 的权值为这个 border 对应后缀的 \(w\) 的最小值。考虑每次加入一个字符后答案的增量,等于加入后所有 border 的权值和。
假设当前加入字符 \(c\),首先如果 \(s_0 = c\),新增一个长度为 \(1\) 的 border,另外,如果一个 border 对应前缀的下一个字符不是 \(c\),则这个 border 需要删除。怎么找要删的 border 呢,我们考虑 fail 树,即 \(i\) 向 \(nxt_i\) 连边,记 \(anc_i\) 表示 \(i\) 的祖先中第一个后继字符和 \(c\) 不同的 border,暴力删除,可以使用单调栈上二分求其权值。由于最多加入 \(n\) 个 border,所以均摊下来每次删除 \(\mathcal{O}(1)\) 个。
不被删除的 border 都会往后拓展,那么如果其权值 \(> w_i\) 就需要修改为 \(w_i\),开一个 map 暴力跳是对的,因为总共有 \(\mathcal{O}(n)\) 种 \(w\),每次修改至少减少一种。
时间复杂度 \(\mathcal{O}(n \log n)\)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 i128;
typedef pair<int, int> pii;
const int N = 6e5 + 10, mod = 998244353;
template<typename T>
void dbg(const T &t) { cout << t << endl; }
template<typename Type, typename... Types>
void dbg(const Type& arg, const Types&... args) {cout << arg << ' ';dbg(args...);
}
namespace Loop1st {
int n, nxt[N], anc[N], w[N];
char c;
ll sum;
i128 ans;
vector<int>stk;
string s;
map<int, int>cnt;
int ask(int pos) {return w[*lower_bound(stk.begin(), stk.end(), pos)];
}
void print(i128 num) {if (num >= 10) print(num / 10);cout << (int)(num % 10);
}
void main() {cin >> n;cin >> c >> w[0];s += c;stk.push_back(0);ans = w[0];cout << w[0] << '\n';for (int i = 1, j = 0; i < n; i++) {cin >> c >> w[i];c = (ans + c - 'a') % 26 + 'a';w[i] ^= ans & ((1 << 30) - 1);s += c;while (j && s[j] != c) j = nxt[j];if (s[j] == c) 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;) {if (s[k] == c) k = anc[k];else {int v = ask(i - k); // 删除一个长度为 k 的 border, 查询 [i - k, i - 1] 的后缀 mincnt[v]--;sum -= v;if (!cnt[v]) cnt.erase(v);k = nxt[k];}}if (s[0] == c) cnt[w[i]]++, sum += w[i];while (!stk.empty() && w[stk.back()] >= w[i]) stk.pop_back();stk.push_back(i);int del = 0;for (auto it = cnt.upper_bound(w[i]); it != cnt.end(); ) {auto [x, y] = *it;del += y;sum -= (ll)y * (x - w[i]);auto tmp = next(it);cnt.erase(it);it = tmp;}cnt[w[i]] += del;ans += sum + w[stk[0]]; // 别忘了整个串也要统计print(ans);cout << '\n';}
}}
int main() {// freopen("data.in", "r", stdin);// freopen("data.out", "w", stdout);ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);int T = 1;// cin >> T;while (T--) Loop1st::main();return 0;
}
// start coding at 13:53
// finish debugging at 14:33