[NOI2016]優秀的拆分 後綴自動機 樹上啓發式合併 線段樹

[NOI2016]優秀的拆分

題目傳送門
luogu
bzoj

分析

這道題不管採用Hash,後綴數組還是自動機,網上大部分的題解都採用了關鍵點+調和級數這個操作。本蒟蒻想不到關鍵點這個操作,所以採用的是一種較爲繁瑣的O(nlog2)O(nlog^2)做法。

首先肯定將問題轉化成對於每個ii求以ii爲邊界的AAAA結構個數,當然前綴後綴分別求一遍,以下默認是前綴。

考慮形式化這個問題,對於某個前綴ii,求所有的前綴j(j<i)j(j<i),使得i,ji,j的最長公共後綴的長度大於jij-i,也就是{jj<i,ijLcs(S1,j,S1,i)}|\{j|j<i,i-j\le |Lcs(S_{1,j},S_{1,i})|\}|

對於後綴前綴的問題,我們一般將他們放到後綴自動機的parentparent樹上考慮,由於後綴自動機的parentparent樹相當於是將每個前綴逆序插入TrieTrie,所以某兩個前綴的LcsLcs對應的就是他們parentparent樹上的LcaLca

所以轉化成樹上給若干個關鍵點(對應的是字符串的前綴),對於每個xx,求{ymxxmxymxlca(x,y)}\{y|mx_x-mx_y\le mx_{lca(x,y)}\}

考慮採用樹上啓發式合併,對於每個節點建立一顆動態開點線段樹,我們讓父親繼承重兒子的線段樹,把其他子樹中的線段樹的節點暴力插入合併。

插入某個節點的時候,考慮線段樹內的節點對其的貢獻,和它對線段樹內節點的貢獻。前者用一個區間詢問即可,否則在線段樹上打標記,但這個線段樹要被拆開的時候再把暴力標記推下去貢獻到答案上即可。

每個節點插入的之後其所在線段樹大小翻倍,所以之多插入loglog次,總複雜度O(nlog2)O(nlog^2)

代碼

#include<bits/stdc++.h>
const int N = 6e4 + 10, T = 1e6 + 10;
int ri() {
	char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
	for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int nx[N], pr[N], fa[N], ch[N][26], st[N], rt[N], mx[N], ans1[N], ans2[N];
int ls[T], rs[T], cnt[T], tag[T], tp, *tot, top, n, last, sz;
bool val[N]; char s[N];
void Clear() {
	top = last = 1; pr[1] = 0;
	memset(ch[1], 0, sizeof(ch[1]));
	sz = 0;
	for(int i = 1;i <= n; ++i)
		tot[i] = 0;
}
void Push(int p) {
	if(tag[p]) {
		if(ls[p])
			tag[ls[p]] += tag[p];
		if(rs[p])
			tag[rs[p]] += tag[p];
		tag[p] = 0;
	}
}
void Get(int p, int L, int R) {
	if(L == R) {
		tot[L] += tag[p];
		st[++tp] = L;
		return ;
	}
	int m = L + R >> 1; Push(p);
	if(ls[p])
		Get(ls[p], L, m);
	if(rs[p])
		Get(rs[p], m + 1, R);
}
void Ins(int &p, int L, int R, int x) {
	if(!p) {p = ++sz; ls[p] = rs[p] = tag[p] = cnt[p] = 0;}
	++cnt[p]; if(L == R) return ;
	int m = L + R >> 1; Push(p);
	if(x <= m) Ins(ls[p], L, m, x);
	else Ins(rs[p], m + 1, R, x);
}
void Modify(int p, int L, int R, int st, int ed) {
	if(L == st && ed == R)
		return ++tag[p], void();
	int m = L + R >> 1; Push(p);
	if(st <= m && ls[p])
		Modify(ls[p], L, m, st, std::min(ed, m));
	if(ed > m && rs[p])
		Modify(rs[p], m + 1, R, std::max(st, m + 1), ed);
}
int Query(int p, int L, int R, int st, int ed) {
	if(L == st && ed == R)
		return cnt[p];
	int m = L + R >> 1, ans = 0;
	if(st <= m && ls[p])
		ans += Query(ls[p], L, m, st, std::min(ed, m));
	if(ed > m && rs[p])
		ans += Query(rs[p], m + 1, R, std::max(st, m + 1), ed);
	return ans;
}
void Extend(int c) {
	int p = last, np = last = ++top; 
	memset(ch[np], 0, sizeof(ch[np])); pr[np] = 0;
	mx[np] = mx[p] + 1; rt[np] = 0; val[np] = true;
	for(;p && !ch[p][c]; p = fa[p])
		ch[p][c] = np;
	if(!p) fa[np] = 1;
	else {
		int q = ch[p][c];
		if(mx[q] == mx[p] + 1) 
			fa[np] = q;
		else {
			int nq = ++top; mx[nq] = mx[p] + 1;
			memcpy(ch[nq], ch[q], sizeof(ch[nq]));
			rt[nq]  = 0; val[nq] = false; pr[nq] = 0;
			fa[nq] = fa[q];
			fa[q] = fa[np] = nq;
			for(;ch[p][c] == q; p = fa[p])
				ch[p][c] = nq;
		}
	}
}
void Merge(int rt, int x, int c) {
	if(x - c)
		tot[x] += Query(rt, 1, n, x - c, x);
	Modify(rt, 1, n, x, std::min(n, x + c));
}
void Dfs(int u) {
	if(!pr[u])
		return Ins(rt[u], 1, n, mx[u]);
	int ds = 0;
	for(int i = pr[u]; i; i = nx[i]) {
		Dfs(i);
		if(cnt[rt[i]] > cnt[rt[ds]])
			ds = i;
	}
	rt[u] = rt[ds];
	for(int i = pr[u]; i; i = nx[i]) 
		if(i != ds){
			tp = 0; Get(rt[i], 1, n);
			for(int x = 1; x <= tp; ++x)
				Merge(rt[u], st[x], mx[u]);
			for(int x = 1;	x <= tp; ++x)
				Ins(rt[u], 1, n, st[x]);
		}
	if(val[u]) {
		Merge(rt[u], mx[u], mx[u]); 
		Ins(rt[u], 1, n, mx[u]);
	}
}
void Work() {
	Clear();
	for(int i = 1; i <= n; ++i)
		Extend(s[i] - 'a');
	for(int i = 2;i <= top; ++i)
		nx[i] = pr[fa[i]], pr[fa[i]] = i;
	Dfs(1); tp = 0; Get(rt[1], 1, n);
}
int main() {
	for(int T = ri(); T--; ) {
		scanf("%s", s + 1); n = strlen(s + 1);
		tot = ans1; Work();
		std::reverse(s + 1, s + n + 1);
		tot = ans2; Work();
		long long ans = 0;
		for(int i = 1;i <= n; ++i)
			ans += 1LL * ans1[i] * ans2[n - i];
		printf("%lld\n", ans);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章