2024 ICPC 南京 H Border Jump 2

赛时只有蒋老师和杜老师通过了该题,笔者SA写法也是在cf被卡常了qoj能过。总体来说是一道很优秀的题目,并且有一定的思考和代码难度。

注意到从前往后考虑 $ S $ 串不容易维护。我们考虑从最后一个状态出发,最后的 $ S $ 串一定存在是一个字符的情况。然后我们构成出反串 $ S^R $ ,上一个状态可能是由 $ t S w S ^ R t ^ R $ 组成,这里有两种情况。

  1. 所有情况,那么贪心考虑, \(t\)\(t^R\) 一定不存在。我们只需要寻找最靠前的 \(S^R\) 即可,左边界不变(\(w\) 可能为空)。
  2. 若 $ S $ 是回文串且 $ S $前面一个字符和后面一个字符相等,那么 \(t S t\) 依旧是回文串,左边界左移1位。若$ t S $ 依旧是回文串,左边界左移1位。

注意到操作1的 \(w\) 串为空时,新串为回文串,否则 \(S\) 的长度至少翻倍。那么操作1在 \(w\) 不为空的操作次数最多执行 \(log\) 次。

注意到对于回文子串 $ T $ 可以写成 $ t w t^R $,在 \(t\) 中选择的左端点进行操作1,一定能匹配到 \(t^R\) 中的部分。若再执行操作2,一定劣于从 \(w\) 开始执行操作2,且一定能执行出相同的左右端点。这里显然发现对于最多的操作次数一定为形如 \(222...2111...1\) 。因为若操作1形成回文串,不如直接从回文中心开始构造。

再次注意到,对于回文串,考虑从回文串最左边开始匹配进行操作1,一定是匹配 \(w\) 为空的新状态,即对于操作结束后相同的左端点可以执行操作2那么一定执行操作2。

那么我们用回文树/马拉车先维护出所有左端点开始操作最多的回文串。再暴力执行1操作,考虑次数即可。

这里提一下操作1的实现。可以用SA+二分找到\(t^R\)\(rk\)范围。然后用主席树二分找大于这个位置的最小值。但是这个写法常数比较大,在cf被卡常了。我们可以用SAM或者exSAM,然后使用线段树合并,在线二维数点等方法找到大于这个位置的最小值。

时间复杂度都是\(O(nlognlogn)\)

code SA + 二分主席树
#include <bits/stdc++.h>
#define ll long long
#define enl putchar('\n')
#define all(x) (x).begin(),(x).end()
#define debug(x) printf(" "#x":%d\n",x);
using namespace std;
const int MAXN = 2e5 + 5, LOG = 19;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
typedef pair<int, int> pii;
char buf[1 << 21], * p1 = buf, * p2 = buf, obuf[1 << 21], * o = obuf, of[35];
#define gc()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
inline ll qpow(ll a, ll n) { ll res = 1; while (n) { if (n & 1)res = res * a % mod; n >>= 1; a = a * a % mod; }return res; }
template <class T = int>inline T read() { T s = 0, f = 1; char c = gc(); for (; !isdigit(c); c = gc())if (c == '-')f = -1; for (; isdigit(c); c = gc())s = s * 10 + c - '0'; return s * f; }
inline void read(int* a, int n) { for (int i = 1; i <= n; ++i)a[i] = read(); }
inline int inal(char* s) { int n = 0; for (s[0] = gc(); !isalpha(s[0]); s[0] = gc()); for (; isalpha(s[n]); s[++n] = gc()); return s[n] = 0, n; }
inline int indi(char* s) { int n = 0; for (s[0] = gc(); !isdigit(s[0]); s[0] = gc()); for (; isdigit(s[n]); s[++n] = gc()); return s[n] = 0, n; }
inline void outd(auto* a, int n) { for (int i = 1; i <= n; ++i)printf("%d ", a[i]); enl; }
int n, m, q;
char s[MAXN], str[MAXN];
int f[MAXN], com[MAXN];

struct HJT {
    int T[MAXN], sum[MAXN * LOG], L[MAXN * LOG], R[MAXN * LOG];
    int tot, n;
    void clear(int n) {
        memset(T + 1, 0, sizeof(int) * n);
        tot = 0;
    }
    void insert(int l, int r, int p, int& u, const int& tar) {
        sum[++tot] = sum[p] + 1;
        L[tot] = L[p]; R[tot] = R[p];
        u = tot;
        if (l == r)return;
        int m = (l + r) >> 1;
        if (tar > m)insert(m + 1, r, R[p], R[u], tar);
        else insert(l, m, L[p], L[u], tar);
    }
    int query(const int& l, const int& r, const int& qv, const int& u, const int& p) {
        if (l == r)return l;
        int m = (l + r) >> 1, res = 0;
        int lv = sum[L[u]] - sum[L[p]], rv = sum[R[u]] - sum[R[p]];
        if (qv <= m && lv)res = query(l, m, qv, L[u], L[p]);
        if (!res && rv && r != n)res = query(m + 1, r, qv, R[u], R[p]);
        return res;
    }
};

struct Sa {     //俩字符串连一起记得开俩倍空间
    char s[MAXN];
    int SA[MAXN], rk[MAXN], oldrk[MAXN << 1], id[MAXN], key1[MAXN], cnt[MAXN];  //SA排名为i对应原字符串下标,rk下标为i的后缀排名
    int height[MAXN], n;    //height与上一个排名相同的个数,height[1]=0
    HJT hjt;
    void clear() {
        hjt.clear(n);
        memset(cnt, 0, sizeof(int) * max(n + 5, 128));
        memset(rk + 1, 0, sizeof(int) * n);
    }
    inline bool cmp(int x, int y, int w) {
        return oldrk[x] == oldrk[y] && oldrk[x + w] == oldrk[y + w];
    }
    inline void getSA() {
        int m = 127;
        for (int i = 1; i <= n; ++i)++cnt[rk[i] = s[i]];
        for (int i = 1; i <= m; ++i)cnt[i] += cnt[i - 1];
        for (int i = n; i >= 1; --i)SA[cnt[rk[i]]--] = i;
        for (int len = 1, p;; len <<= 1, m = p) {
            p = 0;
            for (int i = n; i > n - len; --i)id[++p] = i;
            for (int i = 1; i <= n; ++i)
                if (SA[i] > len)id[++p] = SA[i] - len;
            memset(cnt + 1, 0, sizeof(int) * m);
            for (int i = 1; i <= n; ++i)++cnt[key1[i] = rk[id[i]]];
            for (int i = 1; i <= m; ++i)cnt[i] += cnt[i - 1];
            for (int i = n; i >= 1; --i)SA[cnt[key1[i]]--] = id[i];
            memcpy(oldrk + 1, rk + 1, n * sizeof(int));
            p = 0;
            for (int i = 1; i <= n; ++i)
                rk[SA[i]] = cmp(SA[i], SA[i - 1], len) ? p : ++p;
            if (p == n)break;
        }
        hjt.n = n;
        for (int i = 1; i <= n; ++i)
            hjt.insert(1, n, hjt.T[i - 1], hjt.T[i], SA[i]);
    }

    inline void getHeight() {
        for (int i = 1, k = 0; i <= n; ++i) {
            if (rk[i] == 0)continue;
            if (k)--k;
            while (s[i + k] == s[SA[rk[i] - 1] + k])++k;
            height[rk[i]] = k;
        }
    }
    int st[MAXN][LOG], lg[MAXN];
    inline void initST() {
        lg[1] = 0;
        for (int i = 2; i <= n; i++)lg[i] = lg[i >> 1] + 1;
        for (int i = 1; i <= n; i++)st[i][0] = height[i];
        for (int j = 1; j <= lg[n]; j++)
            for (int i = 1; i <= n - (1 << j) + 1; i++)
                st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
    }
    inline int RMQ(int x, int y) {
        if (x > y)return 0;
        int t = lg[y - x + 1];
        return min(st[x][t], st[y - (1 << t) + 1][t]);
    }
    inline int LCP(int x, int y) {
        x = rk[x], y = rk[y];
        if (x > y)swap(x, y);
        return RMQ(x + 1, y);
    }
    int findL(int p, int len) {
        int l = 1, r = p;
        while (l < r) {
            int mid = l + r >> 1;
            if (RMQ(mid + 1, p) >= len)r = mid;
            else l = mid + 1;
        }
        return r;
    }
    int findR(int p, int len) {
        int l = p, r = n;
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (RMQ(p + 1, mid) >= len)l = mid;
            else r = mid - 1;
        }
        return l;
    }
    int find_nxt(int l, int r) {
        int len = r - l + 1;
        int L = findL(rk[n - r + 1], len), R = findR(rk[n - r + 1], len);
        int res = hjt.query(1, n, r + 1, hjt.T[L - 1], hjt.T[R]);
        return (res && res <= n / 2) ? res + len - 1 : 0;
    }
}p;



void manacher(char* str, int n) {
    int t = 0;
    for (int i = 1; i <= n; ++i) {
        if (t + f[t] >= i)f[i] = min(f[t - (i - t)], t + f[t] - i);
        while (i - f[i] - 1 > 0 && i + f[i] + 1 <= n && str[i - f[i] - 1] == str[i + f[i] + 1])f[i]++;
        if (f[i] + i > f[t] + t) t = i;
    }
}
vector<array<int, 3>> op[MAXN];

void solve() {
    n = inal(s + 1);
    memcpy(p.s + 1, s + 1, sizeof(char) * n);
    p.s[n + 1] = '#';
    for (int i = 1; i <= n; ++i) p.s[n + i + 1] = s[n - i + 1];
    p.n = n + n + 1;
    p.clear();
    p.getSA();
    p.getHeight();
    p.initST();

    int ans = 0;
    for (int i = 1; i <= n; ++i) op[i].clear();
    for (int i = n; i >= 1; --i)
        com[i] = s[i] == s[i + 1] ? com[i + 1] + 1 : 0;
    str[m = 1] = '#';
    for (int i = 1; i <= n; ++i)
        str[++m] = s[i], str[++m] = '#';
    memset(f + 1, 0, sizeof(int) * m);
    manacher(str, m);
    for (int i = 1; i <= m; ++i) {
        int l = (i - f[i] + 1) / 2, c = (i + 1) / 2, r = (i + f[i]) / 2;
        int f = i & 1 ? -1 : 1;
        int v = min(c + com[c], r);
        op[l].push_back({v, f * c, 1});
        op[c].push_back({v, f * c, 0});
    }
    set<pii>S;
    for (int i = 1; i <= n; ++i) {
        for (auto x : op[i]) {
            if (x[2])S.insert({x[0], x[1]});
            else S.erase(S.find({x[0], x[1]}));
        }
        int v = 0, c = i;
        if (S.size()) {
            auto it = *S.rbegin();
            v = it.first - i;
            if (it.second > 0) c = it.second + it.second - i;
            else c = -it.second - it.second - i - 1;
            c = max(c, it.first);
        }
        while (c = p.find_nxt(i, c))
            ++v;
        ans = max(ans, v);
    }
    printf("%d\n", ans);
    memset(p.s + 1, 0, sizeof(char) * m);
}
signed main(signed argc, char const* argv[]) {
    clock_t c1 = clock();
#ifdef LOCAL
    freopen("in.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    //=============================================================
    int TxT = 1;
    TxT = read();
    while (TxT--)
        solve();
    //=============================================================
#ifdef LOCAL
    end :
    cerr << "Time Used:" << clock() - c1 << "ms" << endl;
#endif
    return 0;
}

code exSAM + 线段树合并
#include <bits/stdc++.h>
#define ll long long
#define enl putchar('\n')
#define all(x) (x).begin(),(x).end()
#define debug(x) printf(" "#x":%d\n",x);
using namespace std;
const int MAXN = 2e5 + 5, LOG = 20;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
typedef pair<int, int> pii;
char buf[1 << 21], * p1 = buf, * p2 = buf, obuf[1 << 21], * o = obuf, of[35];
#define gc()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
inline ll qpow(ll a, ll n) { ll res = 1; while (n) { if (n & 1)res = res * a % mod; n >>= 1; a = a * a % mod; }return res; }
template <class T = int>inline T read() { T s = 0, f = 1; char c = gc(); for (; !isdigit(c); c = gc())if (c == '-')f = -1; for (; isdigit(c); c = gc())s = s * 10 + c - '0'; return s * f; }
inline void read(int* a, int n) { for (int i = 1; i <= n; ++i)a[i] = read(); }
inline int inal(char* s) { int n = 0; for (s[0] = gc(); !isalpha(s[0]); s[0] = gc()); for (; isalpha(s[n]); s[++n] = gc()); return s[n] = 0, n; }
inline int indi(char* s) { int n = 0; for (s[0] = gc(); !isdigit(s[0]); s[0] = gc()); for (; isdigit(s[n]); s[++n] = gc()); return s[n] = 0, n; }
inline void outd(auto* a, int n) { for (int i = 1; i <= n; ++i)printf("%d ", a[i]); enl; }
int n, m, q;
char s[MAXN], str[MAXN];
int f[MAXN], com[MAXN];

struct SGT {
    int L[MAXN * LOG * 8], R[MAXN * LOG * 8], v[MAXN * LOG * 8];
    int T[MAXN << 1], tot;
    void insert(int p, int& x, int l = 1, int r = n) {
        if (!x)x = ++tot;
        v[x]++;
        if (l == r) return;
        int mid = l + r >> 1;
        if (p <= mid)insert(p, L[x], l, mid);
        else insert(p, R[x], mid + 1, r);
    }
    void merge(int& x, int y, int l = 1, int r = n) {
        // if (!x || !y) { x |= y; return; }
        if (!y)return;
        if (!x) { x = ++tot; v[x] = v[y]; L[x] = L[y], R[x] = R[y]; return; }
        v[++tot] = v[y] + v[x];
        L[tot] = L[x];
        R[tot] = R[x];
        x = tot;
        if (l == r) return;
        int mid = l + r >> 1;
        merge(L[x], L[y], l, mid);
        merge(R[x], R[y], mid + 1, r);
        v[x] = v[L[x]] + v[R[x]];
    }
    int query(int x, const int& qv, int l = 1, int r = n) {
        if (l == r)return l;
        int m = (l + r) >> 1, res = 0;
        if (qv <= m && v[L[x]])res = query(L[x], qv, l, m);
        if (!res && v[R[x]])res = query(R[x], qv, m + 1, r);
        return res;
    }
};

struct exSAM : SGT { //最多2n-1个点和3n-4条边
    int len[MAXN << 1], link[MAXN << 1], ch[MAXN << 1][26]; //我们记 longest(v) 为其中最长的一个字符串,记 len(v) 为它的长度。
    int pos[MAXN << 1], rp[MAXN << 1];
    vector<int> G[MAXN << 1];
    int cur, lst, siz;
    exSAM() { clear(); }
    void clear() {  //设置起始点S
        for (int i = 0; i <= siz; ++i) G[i].clear();
        memset(ch, 0, sizeof(int) * (siz + 1) * 26);
        memset(pos, 0, sizeof(int) * (siz + 1));
        memset(rp, 0, sizeof(int) * (siz + 1));
        memset(T, 0, sizeof(int) * (siz + 1));
        memset(L, 0, sizeof(int) * (tot + 1));
        memset(R, 0, sizeof(int) * (tot + 1));
        memset(v, 0, sizeof(int) * (tot + 1));

        tot = 0;
        len[0] = 0;
        link[0] = -1;
        siz = 0;    //siz设置成0实际上有一个点,方便标记
        lst = cur = 0;
    }
    int extend(int c) {
        lst = cur;
        if (ch[lst][c]) {
            int q = ch[lst][c];
            if (len[lst] + 1 == len[q])return cur = q;
            else {
                int clone = ++siz;
                link[clone] = link[q];
                len[clone] = len[lst] + 1;
                link[q] = clone;
                for (; ~lst && ch[lst][c] == q; lst = link[lst])ch[lst][c] = clone;
                memcpy(ch[clone], ch[q], sizeof(ch[q]));
                return cur = clone;
            }
        }
        cur = ++siz;
        len[cur] = len[lst] + 1;
        for (; ~lst && !ch[lst][c]; lst = link[lst])ch[lst][c] = cur;

        if (lst == -1) {
            link[cur] = 0;
        } else {
            int q = ch[lst][c];
            if (len[lst] + 1 == len[q]) {
                link[cur] = q;
            } else {        //克隆的点是q(lst的c后继)
                int clone = ++siz;
                link[clone] = link[q];
                len[clone] = len[lst] + 1;
                link[cur] = link[q] = clone;
                for (; ~lst && ch[lst][c] == q; lst = link[lst])ch[lst][c] = clone;
                memcpy(ch[clone], ch[q], sizeof(ch[q]));
            }
        }
        return cur;
    }
    int fa[MAXN << 1][LOG];
    void dfs(int x) {
        for (int i = 1; i < LOG; ++i)
            fa[x][i] = fa[fa[x][i - 1]][i - 1];
        if (pos[x])insert(pos[x], T[x]);
        for (auto v : G[x]) {
            dfs(v);
            merge(T[x], T[v]);
        }
    }
    void build() {
        for (int i = 1; i <= siz; ++i) {
            G[link[i]].push_back(i);
            fa[i][0] = link[i];
        }
        dfs(0);
    }
    int FindR(int l, int r) {
        int x = rp[l];
        for (int i = LOG - 1; i >= 0; --i)
            if (len[fa[x][i]] >= r - l + 1)
                x = fa[x][i];
        return x;
    }
    int find_nxt(int l, int r) {
        if (r == n)return 0;
        int x = FindR(l, r);
        return query(T[x], r + r - l + 1);
    }
}p;

void manacher(char* str, int n) {
    int t = 0;
    for (int i = 1; i <= n; ++i) {
        if (t + f[t] >= i)f[i] = min(f[t - (i - t)], t + f[t] - i);
        while (i - f[i] - 1 > 0 && i + f[i] + 1 <= n && str[i - f[i] - 1] == str[i + f[i] + 1])f[i]++;
        if (f[i] + i > f[t] + t) t = i;
    }
}
vector<array<int, 3>> op[MAXN];

void solve() {
    p.clear();
    n = inal(s + 1);
    for (int i = 1; i <= n; ++i) {
        int c = p.extend(s[i] - 'a');
        p.pos[c] = i;
    }
    p.cur = 0;
    for (int i = n; i >= 1; --i) {
        int c = p.extend(s[i] - 'a');
        p.rp[i] = c;
    }
    p.build();
    int ans = 0;
    for (int i = 1; i <= n; ++i) op[i].clear();
    for (int i = n; i >= 1; --i)
        com[i] = s[i] == s[i + 1] ? com[i + 1] + 1 : 0;
    str[m = 1] = '#';
    for (int i = 1; i <= n; ++i)
        str[++m] = s[i], str[++m] = '#';
    memset(f + 1, 0, sizeof(int) * m);
    manacher(str, m);
    for (int i = 1; i <= m; ++i) {
        int l = (i - f[i] + 1) / 2, c = (i + 1) / 2, r = (i + f[i]) / 2;
        int f = i & 1 ? -1 : 1;
        int v = min(c + com[c], r);
        op[l].push_back({v, f * c, 1});
        op[c].push_back({v, f * c, 0});
    }
    set<pii>S;
    for (int i = 1; i <= n; ++i) {
        for (auto x : op[i]) {
            if (x[2])S.insert({x[0], x[1]});
            else S.erase(S.find({x[0], x[1]}));
        }
        int v = 0, c = i;
        if (S.size()) {
            auto it = *S.rbegin();
            v = it.first - i;
            if (it.second > 0) c = it.second + it.second - i;
            else c = -it.second - it.second - i - 1;
            c = max(c, it.first);
        }
        while (c = p.find_nxt(i, c))
            ++v;
        ans = max(ans, v);
    }
    printf("%d\n", ans);
}
signed main(signed argc, char const* argv[]) {
    clock_t c1 = clock();
#ifdef LOCAL
    freopen("in.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    //=============================================================
    int TxT = 1;
    TxT = read();
    while (TxT--)
        solve();
    //=============================================================
#ifdef LOCAL
    end :
    cerr << "Time Used:" << clock() - c1 << "ms" << endl;
#endif
    return 0;
}

posted on 2024-11-08 19:14  Quixotica  阅读(83)  评论(0编辑  收藏  举报