【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 ...
随机推荐
- IntelliJ IDEA 下载和激活
IntelliJ IDEA 下载地址: https://www.jetbrains.com/idea/download/#section=windows 激活码获取地址:http://idea.lan ...
- gluoncv 训练自己的数据集,进行目标检测
跑了一晚上的模型,实在占GPU资源,这两天已经有很多小朋友说我了.我选择了其中一个参数. https://github.com/dmlc/gluon-cv/blob/master/scripts/de ...
- 2、Android-UI(布局待完成)
2.3.布局 实现界面的整齐摆放各种控件需要使用布局来完成 布局是一种可用于放置很多控件的容器 可以按照一定的规律调整内部的控件位置 布局的内部不仅可以放置控件还可以放置布局 1.线性布局 Linea ...
- Maven搭建Spring MVC时使用jstl无效
1 Maven引入依赖jar包:jstl.jar和standard.jar <dependency> <groupId>javax.servlet</groupId> ...
- .NET Core多语言
ASP.NET Core中提供了一些本地化服务和中间件,可将网站本地化为不同的语言文化. ASP.NET Core中我们可以使用Microsoft.AspNetCore.Localization库来实 ...
- Mysql查询正在运行的事务
查询 正在执行的事务:SELECT * FROM information_schema.INNODB_TRX 根据这个事务的线程ID(trx_mysql_thread_id): 可以使用mysql命令 ...
- CentOS7安装及相关配置转载
1. 怎样在 CentOS 7.0 上安装和配置 VNC 服务器 https://linux.cn/article-5335-1.html 2. 安装完最小化 RHEL/CentOS 7 后需要做的 ...
- DDL-库的管理
一.创建库create database [if not exists] 库名[ character set 字符集名]; 二.修改库alter database 库名 character set 字 ...
- 如何设计处优秀的Restful API
只知道遵规循矩的程序员是假程序员,任何技术都是不断发明创造改进的. 如何设计处优秀的Restful API? 盲目跟风,设计糟糕的Resful API = 浪费时间 ! 不啰嗦,直接进入技术主题: ...
- 歌词解析&class
class song_song: def __init__(self,lrc_file): # 定义两个字典一个列表备用 self.song_file = lrc_file self.song_lrc ...