洛谷php
UOJnode
BZOJios
UOJ上有組hack數據值得一交數組
後綴數組或後綴自動機,如下是後綴自動機我依然不會後綴數組你敢信……ui
容易發現把原串翻轉後"\(r\)類似"就是兩個前綴的最長公共後綴長度不小於\(r\)spa
因而想到後綴自動機code
後綴自動機上一個節點\(endpos/right\)集合的大小就是知足"\(r\)類似"的位置個數,這個節點\(p\)能夠更新的\(r\)知足\(maxlen_{link(p)} + 1 \le r \le maxlen_p\),能夠線段樹維護get
因而在\(link\)樹/\(parent\)樹上\(dp\)求出每一個節點最大值、次大值、最小值、次小值(由於權值可能爲負),以及\(endpos\)集合的大小便可統計答案string
注意特判\(ans1\)爲零的時候\(ans2\)也爲零it
由於改得比較多,因此有點亂
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAXN 300005 typedef long long LL; const int inf = 0x3f3f3f3f; struct SAM { int idx, last, link[MAXN << 1], maxlen[MAXN << 1], next[MAXN << 1][26], size[MAXN << 1], deg[MAXN << 1]; int mx1[MAXN << 1], mx2[MAXN << 1], mn1[MAXN << 1], mn2[MAXN << 1]; int newnode(); int newnode(int); void build(); int add(int); void work(); }; struct SegmentTree { LL maxv[MAXN << 2], add[MAXN << 2]; void build(int, int, int); void update(int, int, int, int, int, LL, LL); void query(int, int, int, int, LL &, LL &); }; int N, a[MAXN]; LL ans1, ans2; char str[MAXN]; SAM sam; SegmentTree sgt; int main() { //freopen("test.in", "r", stdin); //freopen("test.out", "w", stdout); scanf("%d%s", &N, str); std::reverse(str, str + N); for (int i = N - 1; i >= 0; --i) scanf("%d", a + i); sam.build(); sam.work(); ans1 = (LL)N * (N - 1), ans2 = std::max((LL)sam.mx1[1] * sam.mx2[1], (LL)sam.mn1[1] * sam.mn2[1]); if (!ans1) ans2 = 0; printf("%lld %lld\n", ans1 / 2, ans2); for (int i = 1; i < N; ++i) { ans1 = 0, ans2 = -0x3f3f3f3f3f3f3f3f; sgt.query(1, 1, N, i, ans1, ans2); if (!ans1) ans2 = 0; printf("%lld %lld\n", ans1 / 2, ans2); } return 0; } void SAM::build() { idx = 0, last = newnode(); for (int i = 0; i < N; ++i) { last = add(str[i] - 'a'); mx1[last] = mn1[last] = a[i]; mx2[last] = -inf, mn2[last] = inf; size[last] = 1; } for (int i = 0; i <= idx; ++i) if (!size[i]) mn1[i] = mn2[i] = inf, mx1[i] = mx2[i] = -inf; } int SAM::add(int c) { int np = newnode(), p = last; maxlen[np] = maxlen[last] + 1; while (p && !next[p][c]) next[p][c] = np, p = link[p]; if (!p) link[np] = 1; else { int q = next[p][c]; if (maxlen[q] == maxlen[p] + 1) link[np] = q; else { int nq = newnode(q); maxlen[nq] = maxlen[p] + 1; link[q] = link[np] = nq; while (p && next[p][c] == q) next[p][c] = nq, p = link[p]; } } return np; } int SAM::newnode() { return ++idx; } int SAM::newnode(int x) { ++idx; maxlen[idx] = maxlen[x], link[idx] = link[x]; for (int i = 0; i < 26; ++i) next[idx][i] = next[x][i]; return idx; } void SAM::work() { static int q[MAXN << 1], hd, tl; for (int i = 1; i <= idx; ++i) ++deg[link[i]]; for (int i = 1; i <= idx; ++i) if (!deg[i]) q[tl++] = i; while (hd < tl) { int p = q[hd++], fa = link[p]; size[fa] += size[p]; if (mx1[p] >= mx1[fa]) mx2[fa] = std::max(mx1[fa], mx2[p]), mx1[fa] = mx1[p]; else mx2[fa] = std::max(mx2[fa], mx1[p]); if (mn1[p] <= mn1[fa]) mn2[fa] = std::min(mn1[fa], mn2[p]), mn1[fa] = mn1[p]; else mn2[fa] = std::min(mn2[fa], mn1[p]); if (!(--deg[fa])) q[tl++] = fa; } sgt.build(1, 1, N); for (int i = 2; i <= idx; ++i) { bool t = 0; if (mx1[i] != -inf && mx2[i] != -inf) sgt.update(1, 1, N, maxlen[link[i]] + 1, maxlen[i], size[i], (LL)mx1[i] * mx2[i]), t = 1; if (mn1[i] != inf && mn2[i] != inf) sgt.update(1, 1, N, maxlen[link[i]] + 1, maxlen[i], t ? 0 : size[i], (LL)mn1[i] * mn2[i]); } } void SegmentTree::update(int rt, int L, int R, int l, int r, LL sz, LL v) { if (L >= l && R <= r) add[rt] += sz * (sz - 1), maxv[rt] = std::max(maxv[rt], v); else { int mid = (L + R) >> 1; if (l <= mid) update(rt << 1, L, mid, l, r, sz, v); if (r > mid) update(rt << 1 | 1, mid + 1, R, l, r, sz, v); } } void SegmentTree::query(int rt, int L, int R, int pos, LL &res1, LL &res2) { res1 += add[rt], res2 = std::max(res2, maxv[rt]); if (L == R) return; int mid = (L + R) >> 1; if (pos <= mid) query(rt << 1, L, mid, pos, res1, res2); else query(rt << 1 | 1, mid + 1, R, pos, res1, res2); } void SegmentTree::build(int rt, int L, int R) { maxv[rt] = -0x3f3f3f3f3f3f3f3f, add[rt] = 0; if (L == R) return; int mid = (L + R) >> 1; build(rt << 1, L, mid); build(rt << 1 | 1, mid + 1, R); } //Rhein_E