Loading

CF1286E

很牛的题。

定义一个 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] 的后缀 min
                cnt[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
posted @ 2026-01-06 15:37  循环一号  阅读(4)  评论(0)    收藏  举报