[SDOI2016]生成魔咒

Description

BZOJ 4516
Luogu 4070
逐次从字符串尾添加字符,求每次加入后的本质不同的子串数。

Solution

首先翻转原字符串,这样前缀就变成了后缀。
离线后模拟逐次加入字符的过程。加入第\(i\)个字符时,会增加\(i\)个子串,设已加入的字符中,\(i\)的前驱为\(pre\),后继为\(suc\)
会有\(lcp(pre, i)+lcp(i, suc)\)个重复子串,但是原来的\(lcp(pre, suc)\)已经不算在重复子串中了。

Code

#include <cstdio>
#include <algorithm>
#include <set>

typedef long long ll;
const int N = 1e5 + 10;

int s[N], a[N], h[N], sa[N], rnk[N], tax[N], tmp[N], fr[N];
int n, m;
int tr[N<<2];
std::set<int> fck;

void rsort() {
    for (int i = 1; i <= m; ++i) tax[i] = 0;
    for (int i = 1; i <= n; ++i) ++tax[rnk[i]];
    for (int i = 1; i <= m; ++i) tax[i] += tax[i-1];
    for (int i = n; i >= 1; --i) sa[tax[rnk[tmp[i]]]--] = tmp[i];
}

void ssort() {
    for (int i = 1; i <= n; ++i) rnk[i] = a[i], tmp[i] = i;
    rsort();
    for (int w = 1, p = 0; p < n; w <<= 1) {
        p = 0;
        for (int i = 1; i <= w; ++i) tmp[++p] = n - w + i;
        for (int i = 1; i <= n; ++i) if (sa[i] > w) tmp[++p] = sa[i] - w;
        rsort();
        std::swap(rnk, tmp);
        rnk[sa[1]] = p = 1;
        for (int i = 2; i <= n; ++i) {
            rnk[sa[i]] = (tmp[sa[i]] == tmp[sa[i-1]] && tmp[sa[i]+w] == tmp[sa[i-1]+w])
                        ? p : ++p;
        }
        m = p;
    }
    for (int i = 1, k = 0; i <= n; ++i) {
        while (a[i+k] == a[sa[rnk[i]-1]+k]) ++k;
        h[rnk[i]] = k;
        if (k) --k;
    }
}

inline int ls(int o) { return o<<1; }
inline int rs(int o) { return o<<1|1; }

void build(int o, int l, int r) {
	
	if (l == r) {
		tr[o] = h[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(ls(o), l, mid); build(rs(o), mid+1, r);
	tr[o] = std::min(tr[ls(o)], tr[rs(o)]);
}

int query(int o, int l, int r, int ql, int qr) {
	if (ql <= l && r <= qr)
		return tr[o];
	int mid = (l+r) >> 1;
	int ans = 0x3f3f3f3f;
	if (ql <= mid) ans = std::min(ans, query(ls(o), l, mid, ql, qr));
	if (qr > mid) ans = std::min(ans, query(rs(o), mid+1, r, ql, qr));
	return ans;
}

ll lcp(int x, int y) {
    if (x == y) return 0;
    if (x > y) std::swap(x, y);
	return (ll)query(1, 1, n, x+1, y);
}

int pre(int x) {
	std::set<int>::iterator it = fck.lower_bound(x);
	if (it == fck.begin()) return -1;
	return *(--it);
}
int suc(int x) {
    std::set<int>::iterator it = fck.upper_bound(x);
    if (it == fck.end()) return -1;
    return *(it);
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) scanf("%d", &s[i]), tmp[i] = s[i];
	
	std::sort(s+1, s+n+1);
	m = std::unique(s+1, s+n+1) - s - 1;
	for (int i = 1; i <= n; ++i)
	    a[n-i+1] = std::lower_bound(s+1, s+m+1, tmp[i]) - s;

	ssort();
	build(1, 1, n);
	
	ll ans = 0;
	for (int i = n; i >= 1; --i) {
	    ans += (ll)n - i + 1;
	    fck.insert(rnk[i]);
	    int pr = pre(rnk[i]), sc = suc(rnk[i]);
	    ans -= lcp(pr, rnk[i]) + lcp(rnk[i], sc) - lcp(pr, sc);
	    printf("%lld\n", ans);
	}
	return 0;
}
posted @ 2018-09-03 21:45  wyxwyx  阅读(140)  评论(0编辑  收藏  举报