【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 ...
随机推荐
- 2018-2019-2 网络对抗技术 20165322 Exp6 信息搜集与漏洞扫描
2018-2019-2 网络对抗技术 20165322 Exp6 信息搜集与漏洞扫描 目录 实验原理 实验内容与步骤 各种搜索技巧的应用 DNS IP注册信息的查询 基本的扫描技术 漏洞扫描 基础问题 ...
- 【Autoprefixer】Hbuilder中配置Autoprefixer
1.安装node.js 2.安装Autoprefixer 3.安装postcss-cli 4.配置外部工具 打开HBuilder,运行-外部工具-外部工具配置,如图: 新建一个外部工具配置 名称填写a ...
- eclipse主题皮肤设置
这里先声明,下面的方式适合最新版本的Eclipse Luna,旧的版本可以下载我提供的这个插件,并将其放在eclipse目录下的plugins目录下即可. 插件下载地址:http://download ...
- 关于python接口基础到进阶随笔
想了很久,闲来无事,今天想了下还是总结了下写下来,部分参考官方源码理解,还有就是这么久的理解, 如果觉得有帮助请记得点赞 先讲下接口url组成拿后台服务为例 通常一个后台请求url格式: http:/ ...
- PAT——1038. 统计同成绩学生
本题要求读入N名学生的成绩,将获得某一给定分数的学生人数输出. 输入格式: 输入在第1行给出不超过105的正整数N,即学生总人数.随后1行给出N名学生的百分制整数成绩,中间以空格分隔.最后1行给出要查 ...
- Android App的签名打包(晋级篇)
http://blog.csdn.net/linghu_java/article/details/6701666 Andriod应用程序如果要在手机或模拟器上安装,必须要有签名! 1.签名的意义 为了 ...
- 使用Docker发布.NET CORE API
1.使用VS 2015 新建了一个Core API项目,然后把他的依赖升级到最新(我机器VS 2015默认的包都是rc版本),然后publish. 2.在publish目录的同级目录下,新建Docke ...
- ASP.NET Core MVC中Controller的Action如何直接使用Response.Body的Stream流输出数据
在ASP.NET Core MVC中,我们有时候需要在Controller的Action中直接输出数据到Response.Body这个Stream流中,例如如果我们要输出一个很大的文件到客户端浏览器让 ...
- iOS之限制TextField的输入长度
网上有很多限制textField输入长度方法,但是我觉得都不是很完美,准确来说可以说是不符合实际开发的要求,因此在这里整理一下textField限制输入长度的方法.我所采用的并不是监听方法而是最不同的 ...
- javascript 异步操作,串形执行,并行执行
单线程模型 单线程模型指的是,JavaScript 只在一个线程上运行.也就是说,JavaScript 同时只能执行一个任务,其他任务都必须在后面排队等待. 注意,JavaScript 只在一个线程上 ...