[BZOJ3676][UOJ#103][APIO2014]回文串 Palindromes(Manacher+后缀自动机)

一遍过的人是神。要是会回文树就好了

Source

https://www.lydsy.com/JudgeOnline/problem.php?id=3676
http://uoj.ac/problem/103

Solution

据说这题要用回文树?
不会。
于是用了 SAM + Manacher ,
还卡空间卡了几次。
先考虑,如果只是求所有子串的 × 出 现 次 数 × 长 度 之和,那么这是道后缀自动机裸题,答案是:

maxuMaxlu×|Rightu| max u M a x l u × | R i g h t u |

如果要求所有回文子串的 × 出 现 次 数 × 长 度 之和,那么答案为:
maxuMaxpu×|Rightu| max u M a x p u × | R i g h t u |

其中 Maxpu M a x p u 为状态 u u 能表示出的最长回文子串。
现在考虑怎样求 Maxpu M a x p u
我们知道, SAM 中每个状态表示的子串都是这样的形式:
左端点在 [a,b] [ a , b ] 范围内,右端点为 c c 的所有子串。
因此,问题转化成:对于 l[a,b],r=c l ∈ [ a , b ] , r = c ,满足这个条件的子串 [l,r] [ l , r ] 中,最长的回文子串,也就是以 c c 为右端点,左端点在 [a,b] [ a , b ] 范围内,形成的所有回文子串中,左端点编号的最小值。
先 Manacher 求出 R R 数组。
那么对于一个位置 i i ,条件:存在以 i i 为中心, c c 为右端点的回文串,等价于条件: i+Ric i + R i ≥ c
维护一个序列,序列第 i i 个位置上的数是 i+Ri i + R i
于是问题转化为:每次询问一个区间内,大于等于一个值的第一个数。
可以使用 二分 + RMQ 实现这个询问。

Code

#include 
#include 
#include 
#include 
#include 
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#define SAM(pyz) for (; i && pyz; i = T[i].fa)
using namespace std;
typedef long long ll; const int N = 6e5 + 5, LogN = 21;
int n, m, r[N], QAQ, lst, tmp[N]; char s[N], t[N];
int Log[N], RMQ[N][LogN];
void APIO2014() {
    int p = 0, mx = 0, i, j; For (i, 1, m) {
        r[i] = mx > i ? min(r[(p << 1) - i], mx - i) : 1;
        while (t[i - r[i]] == t[i + r[i]]) r[i]++;
        if (i + r[i] > mx) mx = i + r[i], p = i; RMQ[i][0] = i + r[i] - 1;
    }
    Log[0] = -1; For (i, 1, m) Log[i] = Log[i >> 1] + 1;
    For (j, 1, 20) For (i, 1, m - (1 << j) + 1)
        RMQ[i][j] = max(RMQ[i][j - 1], RMQ[i + (1 << j - 1)][j - 1]);
}
int query(int l, int r) {
    int x = Log[r - l + 1]; return max(RMQ[l][x], RMQ[r - (1 << x) + 1][x]);
}
struct cyx {
    int fa, ri, maxl, go[26], id;
    void init() {fa = ri = maxl = id = 0; memset(go, 0, sizeof(go));}
} T[N];
inline bool comp(const int &a, const int &b) {return T[a].maxl < T[b].maxl;}
void extend(int c) {
    int i = lst; T[lst = ++QAQ].init(); T[lst].ri = 1;
    T[lst].id = T[lst].maxl = T[i].maxl + 1; SAM(!T[i].go[c]) T[i].go[c] = lst;
    if (!i) T[lst].fa = 1; else {
        int j = T[i].go[c]; if (T[i].maxl + 1 == T[j].maxl) T[lst].fa = j;
        else {
            int p; T[p = ++QAQ] = T[j]; T[lst].fa = T[j].fa = p; T[p].ri = 0;
            T[p].maxl = T[i].maxl + 1; SAM(T[i].go[c] == j) T[i].go[c] = p;
        }
    }
}
int qFirst(int l, int r, int x) {
    int le = l; while (l <= r) {
        int mid = l + r >> 1;
        if (query(le, mid) < x) l = mid + 1; else r = mid - 1;
    }
    return l;
}
ll solve() {
    int i; For (i, 1, QAQ) tmp[i] = i; sort(tmp + 1, tmp + QAQ + 1, comp);
    Rof (i, QAQ, 1) T[T[tmp[i]].fa].ri += T[tmp[i]].ri;
    ll ans = 0; For (i, 2, QAQ) {
        int p = T[i].id, le = (p << 1) - T[i].maxl + 1,
        ri = (p << 1) - T[T[i].fa].maxl, w = qFirst(le, ri, p << 1);
        ans = max(ans, 1ll * ((p << 1) + 1 - w) * T[i].ri);
    }
    return ans;
}
int main() {
    int i; scanf("%s", s + 1); n = strlen(s + 1); t[0] = '%';
    T[QAQ = lst = 1].init(); For (i, 1, n) t[++m] = '#', t[++m] = s[i];
    t[++m] = '#'; t[m + 1] = '&'; APIO2014(); For (i, 1, n) extend(s[i] - 'a');
    cout << solve() << endl; return 0;
}

你可能感兴趣的:(BZOJ,UOJ,LOJ)