ABC240Ex Sequence of Substrings

ABC240Ex


模拟赛题。

有意思的。

注意到奇怪的 \(n\leq 2.5 \times 10^4\)

首先可以写出暴力 dp。设 \(f_{i,j}\) 表示考虑长度为 \(i\) 的前缀,最后一次选了 \(s[i - j + 1, i]\) 的贡献。

转移:

\[f_{i, j} = \max_{1\leq x \leq i - j,s[x - y + 1, y] < s[i - j + 1, i]}\{f_{x, y}\} + 1 \]

这是一个二维偏序的形式,但是状态数是 \(O(n^2)\) 的,直接转移复杂度为 \(O(n^2\log n)\)

稍微推一推,发现最优决策中一定有一种情况满足,一个子串的长度如果为 \(len\),那么其后继长度最多为 \(len + 1\),那么所有的答案串长度都无法超过 \(2\sqrt n\),所以有用的状态数只有 \(O(n\sqrt n)\)

于是就做完了,把所有有用状态全部压进一棵 trie 里面,一边遍历一边做二维偏序,复杂度 \(O(n\sqrt n \log n)\)

代码非常好写。

code
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2.5e4 + 10;
const int maxs = 2 * sqrt(2.5e4) + 10;
int lowbit(int x) {return x & (-x);}
int tr[maxn * maxs][2], cnt = 1;
vector<int> loc[maxn * maxs];
int mxl;
int ans;
int n;
class Fenwick {
public:
    int tr[maxn];
    void Update(int x, int val) {
        while(x <= n) {
            tr[x] = max(val, tr[x]);
            x += lowbit(x);
        }
    }
    int Query(int x) {
        int res = 0;
        while(x) {
            res = max(res, tr[x]);
            x -= lowbit(x);
        }
        return res;
    }
}fw;
string s;
void Insert(int x) {
    int p = 1;
    int lim = min(n, x + mxl);
    for(int i = x; i <= lim; i++) {
        int &nxt = tr[p][s[i - 1] - '0'];
        if(!nxt) nxt = ++cnt;
        p = nxt;
        loc[p].emplace_back(i);
    }
}
vector<pair<int, int>> tmp;
void Dfs(int u, int len) {
    tmp.clear();
    for(int i : loc[u]) {
        int f = fw.Query(i - len) + 1;
        ans = max(ans, f);
        tmp.emplace_back(i, f);
    }
    for(auto cur : tmp) {
        fw.Update(cur.first, cur.second);
    }
    for(int i = 0; i < 2; i++) if(tr[u][i]) Dfs(tr[u][i], len + 1);
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    cin >> n >> s;
    mxl = 2 * sqrt(n);
    for(int i = 1; i <= n; i++) {
        Insert(i);
    }
    Dfs(1, 0);
    cout << ans;
}

posted @ 2023-09-21 18:28  N2MENT  阅读(22)  评论(0)    收藏  举报