BZOJ4516: [Sdoi2016]生成魔咒

题目地址

题目地址

题解

很棒的一道后缀数组题!
其实就是对原串求出每一个前缀的本质不同子串的个数。
求一个串的本质不同子串数是一个经典问题,其为\(\frac {n \times (n + 1)} 2 - \sum \text{height[i]}\)
考虑插入前缀的过程,这种情况下的\(\text {sa}\)数组和\(\text {height}\)数组是在不断改变的,这样就很不好统计了。
那么不妨把原串翻转过来,这样加入一个字符其实就是加入一个新的后缀而已。
而我们知道\(\text{LCP(x,y)} = \min_{k=x+1}^y \{\text{height[k]}\}\),所以在后缀没有加全的情况下其实也是可以知道两个串的\(\text {LCP}\)的,只需要预处理一个\(\text{ST}\)表即可。
考虑一下加入一个后缀对当前情况下\(\text {height}\)数组的影响,在进行一次后缀排序后,假设其位置为\(pos\),那么只会影响到\(\text {height[pos]}\)\(\text {height[pos + 1]}\)
所以只需要动态修改这两个东西即可。
这两个东西可以用平衡树来维护。具体的,在每次插入一个数后,找出它的前驱和后继,给\(\sum \text{height[i]}\)加上\(\text{LCP}(pre, pos)\)\(\text{LCP}(pos, next)\)
平衡树可以上\(\text{set}\)

#include 
using namespace std;

typedef long long ll;
const int N = 100010;

ll ans = 0;
int n, m, a[N], b[N], f[N][18], LG[N];
int sa[N], height[N], rnk[N], tp[N], tong[N];
set s;

void radix_sort() {
	for(int i = 1; i <= m; ++i) tong[i] = 0;
	for(int i = 1; i <= n; ++i) tong[rnk[i]]++;
	for(int i = 1; i <= m; ++i) tong[i] += tong[i - 1];
	for(int i = n; i; --i) sa[tong[rnk[tp[i]]]--] = tp[i];
}

void suffix_sort() {
	for(int i = 1; i <= n; ++i) rnk[i] = a[i], tp[i] = i;
	m = n; radix_sort();
	for(int w = 1, p = 1; p < n && w <= n; w <<= 1, m = p) {
		p = 0; 
		for(int i = 1; i <= w; ++i) tp[++p] = n - w + i;
		for(int i = 1; i <= n; ++i) if(sa[i] > w) tp[++p] = sa[i] - w;
		radix_sort(); swap(tp, rnk); rnk[sa[1]] = p = 1;
		for(int i = 2; i <= n; ++i) 
			rnk[sa[i]] = (tp[sa[i]] == tp[sa[i - 1]] && tp[sa[i] + w] == tp[sa[i - 1] + w]) ? p : ++p;
	}
	for(int i = 1, k = 0; i <= n; ++i) {
		if(k) --k;
		int j = sa[rnk[i] - 1];
		while(a[i + k] == a[j + k] && i + k <= n && j + k <= n) ++k;
		height[rnk[i]] = k; 
	}
}

int query(int l, int r) {
	int k = LG[r - l + 1];
	return min(f[l][k], f[r - (1 << k) + 1][k]);
}

int main() {
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i) scanf("%d", &a[i]), b[i] = a[i];
	sort(b + 1, b + n + 1);
	for(int i = 1; i <= n; ++i) a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;
	reverse(a + 1, a + n + 1); suffix_sort();
	LG[2] = 1;
	for(int i = 3; i <= n; ++i) LG[i] = LG[i >> 1] + 1;
	for(int j = 1; j <= n; ++j) f[j][0] = height[j];
	for(int j = 1; j <= 19; ++j) {
		for(int i = 1; i + (1 << j) - 1 <= n; ++i) 
			f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
	}
	ans = 0;
	for(int i = n; i; --i) {
		set::iterator it = s.insert(rnk[i]).first, p1 = it, p2 = it;
		if(it != s.begin()) {
			p1--; 
			ans += query(*p1 + 1, rnk[i]); 
			if(++p2 != s.end()) {
				ans += query(rnk[i] + 1, *p2);
				ans -= query(*p1 + 1, *p2);
			}
		} else if(++p2 != s.end()) 
			ans += query(rnk[i] + 1, *p2);
		printf("%lld\n", 1LL * (n - i + 1) * (n - i + 2) / 2LL - ans);
	}
	return 0;
} 

你可能感兴趣的:(BZOJ4516: [Sdoi2016]生成魔咒)