Loading [MathJax]/jax/element/mml/optable/MathOperators.js

【BZOJ4560】[NOI2016]优秀的拆分

【BZOJ4560】[NOI2016]优秀的拆分

题面

bzoj

洛谷

题解

考虑一个形如AABB的串是由两个形如AA的串拼起来的

那么我们设

f[i]:以位置i为结尾的形如AA串的个数

g[i]:以位置i为开头的形如AA串的个数

题目的难点转化为求f,g

但是,其实我们只要O(n^2)暴力求一下就有95pts了,

所以我们接下来考虑最后的5pts怎么拿:

我们枚举A的长度len

将所有位置为len的倍数的点设为关键点,

则如果一个AA满足要求

这个AA必过两个关键点,

那么我们要算的就是相邻两个关键点对答案的贡献:

记相邻两个关键点为,i,j那么j=i+len

Lcp=lcp(suf(i), suf(j)),Lcs=lcs(pre(i-1),pre(j-1))

那么,如果Lcp+Lcs<len,则不能构成AA

为什么呢?

相当于这样一种情况:

\underbrace{.......i-1}_{Lcs}\;\overbrace{\underbrace{i........}_{Lcp}\;....\underbrace{.......j-1}_{Lcs}}^{len}\;\underbrace{j........}_{Lcp}

这样子是不合法的。

反之,中间两段的Lcp,Lcs会有交,而我们这个A串的终点落在中间长度为Lcp+Lcs-len+1的交上都是可以的

因为这样的话平移一下可以保证紧跟着出现一个不重叠的A

又因为串A起点和终点分别出现的位置是一段区间,所以直接分别在f,g上差分即可

复杂度是调和级数O(nlogn)

具体细节看代码:

#include <iostream> 
#include <cstdio> 
#include <cstdlib> 
#include <cstring> 
#include <cmath> 
#include <algorithm> 
#include <map> 
using namespace std; 
inline int gi() { 
    register int data = 0, w = 1; 
    register char ch = 0; 
    while (!isdigit(ch) && ch != '-') ch = getchar(); 
    if (ch == '-') w = -1, ch = getchar(); 
    while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar(); 
    return w * data; 
} 
const int MAX_N = 3e4 + 5;
char a[MAX_N]; 
int N, lg[MAX_N], f[MAX_N], g[MAX_N]; 
struct SuffixArray { 
	int sa[MAX_N], rnk[MAX_N], lcp[MAX_N]; 
	void buildSA() { 
        #define cmp(i, j, k) (y[i] == y[j] && y[i + k] == y[j + k]) 
		static int x[MAX_N], y[MAX_N], bln[MAX_N];
		memset(sa, 0, sizeof(sa)); 
		memset(rnk, 0, sizeof(rnk)); 
		memset(lcp, 0, sizeof(lcp)); 
		memset(x, 0, sizeof(x)); 
		memset(y, 0, sizeof(y)); 
		memset(bln, 0, sizeof(bln));
		int M = 122; 
		for (int i = 1; i <= N; i++) bln[x[i] = a[i]]++; 
		for (int i = 1; i <= M; i++) bln[i] += bln[i - 1]; 
		for (int i = N; i >= 1; i--) sa[bln[x[i]]--] = i; 
		for (int k = 1; k <= N; k <<= 1) { 
			int p = 0; 
			for (int i = 0; i <= M; i++) y[i] = 0; 
			for (int i = N - k + 1; i <= N; i++) y[++p] = i; 
			for (int i = 1; i <= N; i++) if (sa[i] > k) y[++p] = sa[i] - k;
			for (int i = 0; i <= M; i++) bln[i] = 0; 
			for (int i = 1; i <= N; i++) bln[x[y[i]]]++; 
			for (int i = 1; i <= M; i++) bln[i] += bln[i - 1]; 
			for (int i = N; i >= 1; i--) sa[bln[x[y[i]]]--] = y[i]; 
			swap(x, y); x[sa[1]] = p = 1;
			for (int i = 2; i <= N; i++) x[sa[i]] = cmp(sa[i], sa[i - 1], k) ? p : ++p;
			if (p >= N) break;
			M = p; 
		} 
		for (int i = 1; i <= N; i++) rnk[sa[i]] = i; 
		for (int i = 1, j = 0; i <= N; i++) {
			if (j) j--; 
			while (a[i + j] == a[sa[rnk[i] - 1] + j]) ++j; 
			lcp[rnk[i]] = j; 
		} 
	} 
	int st[16][MAX_N]; 
	void buildST() { 
		memset(st, 63, sizeof(st)); 
		for (int i = 1; i <= N; i++) st[0][i] = lcp[i]; 
		for (int i = 1; i <= 15; i++)
			for (int j = 1; j <= N; j++)
				st[i][j] = min(st[i - 1][j], st[i - 1][j + (1 << (i - 1))]); 
	} 
	int query(int l, int r) { 
		int _l = l, _r = r; 
		l = min(rnk[_l], rnk[_r]) + 1, r = max(rnk[_l], rnk[_r]);
		int t = lg[r - l + 1]; 
		return min(st[t][l], st[t][r - (1 << t) + 1]); 
	} 
} A, B; 
void Sol() {
	scanf("%s", a + 1); N = strlen(a + 1); 
	A.buildSA(), A.buildST(); 
	reverse(&a[1], &a[N + 1]); 
	B.buildSA(), B.buildST(); 
	memset(f, 0, sizeof(f)); 
	memset(g, 0, sizeof(g)); 
	for (int Len = 1; Len <= N / 2; Len++) { 
		for (int i = Len, j = i + Len; j <= N; i += Len, j += Len) { 
			int Lcp = min(A.query(i, j), Len), Lcs = min(B.query(N - i + 2, N - j + 2), Len - 1); 
			int t = Lcp + Lcs - Len + 1; 
			if (Lcp + Lcs >= Len) {
				g[i - Lcs]++, g[i - Lcs + t]--; 
				f[j + Lcp - t]++, f[j + Lcp]--; 
			} 
		} 
	} 
	for (int i = 1; i <= N; i++) f[i] += f[i - 1], g[i] += g[i - 1]; 
	long long ans = 0; 
	for (int i = 1; i < N; i++) ans += 1ll * f[i] * g[i + 1]; 
	printf("%lld\n", ans); 
} 
int main () { 
#ifndef ONLINE_JUDGE 
    freopen("cpp.in", "r", stdin); 
#endif 
	for (int i = 2; i <= 30000; i++) lg[i] = lg[i >> 1] + 1; 
	int T; scanf("%d", &T);
	while (T--) Sol(); 
	return 0; 
} 
C++
posted @   heyujun  阅读(865)  评论(0编辑  收藏  举报
编辑推荐:
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
· 对象命名为何需要避免'-er'和'-or'后缀
· SQL Server如何跟踪自动统计信息更新?
· AI与.NET技术实操系列:使用Catalyst进行自然语言处理
阅读排行:
· dotnet 源代码生成器分析器入门
· 官方的 MCP C# SDK:csharp-sdk
· 一款 .NET 开源、功能强大的远程连接管理工具,支持 RDP、VNC、SSH 等多种主流协议!
· 一步一步教你部署ktransformers,大内存单显卡用上Deepseek-R1
· 一次Java后端服务间歇性响应慢的问题排查记录
点击右上角即可分享
微信分享提示