BZOJ4199[NOI2015]品酒大会(后缀数组/后缀自动机+线段树)

题目链接

洛谷

UOJ

BZOJ

UOJ上有组hack数据值得一交

解析

后缀数组或后缀自动机,以下是后缀自动机我依然不会后缀数组你敢信……

容易发现把原串翻转后"\(r\)相似"就是两个前缀的最长公共后缀长度不小于\(r\)

于是想到后缀自动机

后缀自动机上一个节点\(endpos/right\)集合的大小就是满足"\(r\)相似"的位置个数,这个节点\(p\)可以更新的\(r\)满足\(maxlen_{link(p)} + 1 \le r \le maxlen_p\),可以线段树维护

于是在\(link\)树/\(parent\)树上\(dp\)求出每个节点最大值、次大值、最小值、次小值(因为权值可能为负),以及\(endpos\)集合的大小即可统计答案

注意特判\(ans1\)为零的时候\(ans2\)也为零

代码

因为改得比较多,所以有点乱

#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

猜你喜欢

转载自www.cnblogs.com/Rhein-E/p/10643202.html