【BZOJ4560】[NOI2016]优秀的拆分
【BZOJ4560】[NOI2016]优秀的拆分
题面
题解
考虑一个形如\(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\)
为什么呢?
相当于这样一种情况:
\]
这样子是不合法的。
反之,中间两段的\(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;
}
【BZOJ4560】[NOI2016]优秀的拆分的更多相关文章
- [NOI2016]优秀的拆分&&BZOJ2119股市的预测
[NOI2016]优秀的拆分 https://www.lydsy.com/JudgeOnline/problem.php?id=4650 题解 如果我们能够统计出一个数组a,一个数组b,a[i]表示以 ...
- luogu1117 [NOI2016]优秀的拆分
luogu1117 [NOI2016]优秀的拆分 https://www.luogu.org/problemnew/show/P1117 后缀数组我忘了. 此题哈希可解决95分(= =) 设\(l_i ...
- [UOJ#219][BZOJ4650][Noi2016]优秀的拆分
[UOJ#219][BZOJ4650][Noi2016]优秀的拆分 试题描述 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 A 和 B 是任意非空字符串,则我们称该字符串的这种拆分是优秀 ...
- [NOI2016]优秀的拆分(SA数组)
[NOI2016]优秀的拆分 题目描述 如果一个字符串可以被拆分为 \(AABB\) 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 \(aabaaba ...
- 题解-NOI2016 优秀的拆分
NOI2016 优秀的拆分 \(T\) 组测试数据.求字符串 \(s\) 的所有子串拆成 \(AABB\) 形式的方案总和. 数据范围:\(1\le T\le 10\),\(1\le n\le 3\c ...
- [NOI2016]优秀的拆分 后缀数组
题面:洛谷 题解: 因为对于原串的每个长度不一定等于len的拆分而言,如果合法,它将只会被对应的子串统计贡献. 所以子串这个限制相当于是没有的. 所以我们只需要对于每个位置i求出f[i]表示以i为开头 ...
- [BZOJ]4650: [Noi2016]优秀的拆分
Time Limit: 30 Sec Memory Limit: 512 MB Description 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 AA 和 BB 是任意非空字符串, ...
- [Noi2016]优秀的拆分
来自F allDream的博客,未经允许,请勿转载,谢谢. 如果一个字符串可以被拆分为 AABB 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 aab ...
- 【刷题】BZOJ 4650 [Noi2016]优秀的拆分
Description 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 AA 和 BB 是任意非空字符串,则我们称该字符串的这种拆分是优秀的.例如,对于字符串 aabaabaa,如果令 A ...
随机推荐
- BZOJ3566:[SHOI2014]概率充电器(树形DP,概率期望)
Description 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品——概率充电器: “采用全新纳米级加工技术,实现元件与导线能否通电完全由真随机数决定!SHOI 概率充电器, ...
- SQL进价2:三值逻辑和null
1.SQL中的bool类型的值有三种 普通编程语言里的布尔型只有 true 和 false 两个值,这种逻辑体系被称为二值逻辑.而 SQL 语言里,除此之外还有第三个值 unknown,因此这种逻辑体 ...
- POJ 3261 Milk Patterns 【后缀数组 最长可重叠子串】
题目题目:http://poj.org/problem?id=3261 Milk Patterns Time Limit: 5000MS Memory Limit: 65536K Total Subm ...
- JQuery资源网站(收藏)
收藏几个不错的JQuery资源网站,以后备用,给共享者点个赞!!! http://jquery.com/ http://www.htmleaf.com/ http://www.oschina.net/ ...
- web测试--测试点
转载自:51Testing 首先,查找需求说明.网站设计等相关文档,分析测试需求,制定测试计划,确定测试范围和测试策略,一般包括以下几个部分:功能性测试:界面测试:性能测试:数据库测试:安全性测试:兼 ...
- 数据库中间件mycat安装与使用
1.下载 # wget http://dl.mycat.io/1.6-RELEASE/Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz 2.安装 ...
- MVC——MVP——MVVM
MVC什么样? 从这个图中可以清楚的看到: View:视图层——这里是用户与之交互的界面. Model:模型层——这里面主要就是业务数据,并把数据提供给视图层 Controller:控制器——他的主要 ...
- js随笔--关于数组
1.split()将一个字符串分割成字符串数组 stringObject.split(separator,howmany) separator:必需,字符串或正则表达式,从该参数指定的地方分割stri ...
- MySQL----MySQL数据库入门----第五章 多表操作
5.1 外键 比如说有两个数据表,分别是学生信息表student和年级表grade.在student表中有存储学生年级的字段gid(外键),在grade表也有存储学生年级的字段id(主键),stude ...
- php之微型博客的创建
一,微型博客的开发思路 微型博客的创建,确定无疑我们会用到PHP和mysql之间的增添删改查,首先来看一下思维导图: 搭建好计算机里的apache php 和mysql的联动功能,打开phpmyadm ...