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
", 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
", 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
原文地址:https://www.cnblogs.com/Rhein-E/p/10643202.html