【GDOI2020模拟4.11】黑红兔(brr) (SA+字符串性质+可持久化线段树)

\(n\le 500000\)
https://gmoj.net/senior/#main/show/6555

一个发现是我们肯定可以选长度为\(1,2,3,…\)的一组解。

那么长度不超过\(\sqrt {2n}\),做一个dp,设\(f[i][j]\)表示\(j\)为开头,长度为\(i\),是否可以。
用hash去找相同的子串,用个指针维护能转移区域的max值,即可做到\(O(n \sqrt n)\)

接下来需要发现更深的性质:
考虑以\(j\)开头的,如果有长度为\(i\)的解,那么也一定有长度为\(i-1\)的解,所以直接设\(f[j]\)表示以j开头最长是多少。

从后往前做,对于\(f[j]\)的值,二分\(mid\),相当于在\(k\in[j+mid,n]\)中找一个\(k\),满足\(max(lcp(s[j..n],s[k..n]),lcp[s[j+1..n],s[k..n]])\ge mid-1\),且\(f[k]+1\ge mid\)

\(lcp\)的限制建出\(sa\)之后就是在一个区间里,至于\(k\in[j+mid,n]\),用可持久化线段树找区间最大值就好了。

\(O(n~log^2~n)\)还是过不了的。

类似于\(SA\)\(height\)的求法,对\(f\)显然有\(f[j+1]\ge f[j]-1->f[j]\le f[j+1]+1\),这样就不用二分了。


Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int N = 5e5 + 5;

int n;
char s[N];

int m, sa[N], rk[N], tp[N], tax[N], he[N];
void rsort() {
	fo(i, 1, m) tax[i] = 0;
	fo(i, 1, n) tax[rk[tp[i]]] ++;
	fo(i, 1, m) tax[i] += tax[i - 1];
	fd(i, n, 1) sa[tax[rk[tp[i]]] --] = tp[i];
}
int cmp(int *f, int x, int y, int z) { return f[x] == f[y] && f[x + z] == f[y + z];}
int f[20][N];
void build() {
	s[n + 1] = -1;
	fo(i, 1, n) rk[i] = s[i], tp[i] = i;
	m = 255, rsort();
	for(int w = 1, p = 0; p < n; w *= 2) {
		p = 0; fo(i, n - w + 1, n) tp[++ p] = i;
		fo(i, 1, n) if(sa[i] > w) tp[++ p] = sa[i] - w;
		m = p, rsort();
		fo(i, 1, n) tp[i] = rk[i];
		rk[sa[1]] = p = 1;
		fo(i, 2, n) rk[sa[i]] = cmp(tp, sa[i - 1], sa[i], w) ? p : ++ p;
	}
	int j, k = 0;
	for(int i = 1; i <= n; he[rk[i ++]] = k) {
		for(k ? k -- : 0, j = sa[rk[i] - 1]; s[i + k] == s[j + k]; k ++);
	}
	fo(i, 1, n) f[0][i] = he[i];
	fo(j, 1, 19) fo(i, 1, n) {
		f[j][i] = f[j - 1][i];
		if(i + (1 << j - 1) <= n) f[j][i] = min(f[j][i], f[j - 1][i + (1 << j - 1)]);
	}
//	fo(i, 1, n) {
//		pp("%d\n", he[i]);
//		fo(j, sa[i], n) pp("%c", s[j]);
//		hh;
//	}
}

int qu(int x, int y) {
	int l = log2(y - x + 1);
	return min(f[l][x], f[l][y - (1 << l) + 1]);
}

#define i0 t[i].l
#define i1 t[i].r
struct tree {
	int l, r, x;
} t[N * 20]; int tt;
int pl, pr, px;
void add(int &i, int x, int y) {
	if(y < pl || x > pr) return;
	t[++ tt] = t[i]; i = tt;
	t[i].x = max(t[i].x, px);
	if(x == y) return;
	int m = x + y >> 1;
	add(i0, x, m); add(i1, m + 1, y);
}
void ft(int i, int x, int y) {
	if(!i || y < pl || x > pr) return;
	if(x >= pl && y <= pr) { px = max(px, t[i].x); return;}
	int m = x + y >> 1;
	ft(i0, x, m); ft(i1, m + 1, y);
}

int g[N], dp[N];

void cx(int x, int v) {
	pl = x, pr = x;
	for(int l = 1, r = x - 1; l <= r; ) {
		int m = l + r >> 1;
		if(qu(m + 1, x) >= v - 1) pl = m, r = m - 1; else l = m + 1;
	}
	for(int l = x + 1, r = n; l <= r; ) {
		int m = l + r >> 1;
		if(qu(x + 1, m) >= v - 1) pr = m, l = m + 1; else r = m - 1;
	}
}

int pd(int i, int v) {
	px = 0;
	if(i + v <= n) {
		cx(rk[i], v);
		ft(g[i + v], 1, n);
		if(i < n) {
			cx(rk[i + 1], v);
			ft(g[i + v], 1, n);
		}
	}
	return px + 1 >= v;
}

void work() {
	int ans = 0;
	fd(i, n, 1) {
		dp[i] = dp[i + 1] + 1;
		while(!pd(i, dp[i])) dp[i] --;
		ans = max(ans, dp[i]);
		g[i] = g[i + 1];
		pl = pr = rk[i]; px = dp[i];
		add(g[i], 1, n);
	}
	pp("%d\n", ans);
}

int main() {
	freopen("brr.in", "r", stdin);
	freopen("brr.out", "w", stdout);
	scanf("%s", s + 1);
	n = strlen(s + 1);
	build();
	work();
}
posted @ 2020-04-11 14:20  Cold_Chair  阅读(222)  评论(0编辑  收藏  举报