P4070 [SDOI2016]生成魔咒
题目地址:P4070 [SDOI2016]生成魔咒
相信看到题目之后很多人跟我的思路是一样的——
肯定要用 SA(P3809 【模板】后缀排序)
肯定要会求本质不同的子串个数(P2408 不同子串个数)
然后?就不会了......
瓶颈在哪儿?
你会发现每往后添加一个字符,整个 sa 数组只会插入一个数,要维护不难
但是 height 会无规律变化,这就导致无法高效维护
怎么办呢?
倒置字符串
我们将整个字符串倒置过来
显然本质不同的子串个数不会变化
而每往前添加一个字符串, height 的变化是 \(O(1)\) 的
那么,问题就变得简单很多了
具体实现请看代码注释
#include <bits/stdc++.h>
#define ll long long
#define si set<int>::iterator
using namespace std;
const int N = 1e5 + 6;
int n, m, a[N], b[N];
int sa[N], rk[N], tp[N], tx[N], he[N], st[N][20];
ll ans;
set<int> s;
inline void tsort() {//基数排序
for (int i = 1; i <= m; i++) tx[i] = 0;
for (int i = 1; i <= n; i++) ++tx[rk[i]];
for (int i = 1; i <= m; i++) tx[i] += tx[i-1];
for (int i = n; i; i--) sa[tx[rk[tp[i]]]--] = tp[i];
}
inline bool pd(int i, int w) {
return tp[sa[i-1]] == tp[sa[i]] && tp[sa[i-1]+w] == tp[sa[i]+w];
}
inline void SA() {//后缀数组板子
for (int i = 1; i <= n; i++) {
rk[i] = a[i] = lower_bound(b + 1, b + m + 1, a[i]) - b;
tp[i] = i;
}
tsort();
for (int w = 1, p = 0; p < n; m = p, w <<= 1) {
p = 0;
for (int i = 1; i <= w; i++) tp[++p] = n - w + i;
for (int i = 1; i <= n; i++)
if (sa[i] > w) tp[++p] = sa[i] - w;
tsort();
swap(rk, tp);
rk[sa[1]] = p = 1;
for (int i = 2; i <= n; i++)
rk[sa[i]] = pd(i, w) ? p : ++p;
}
int p = 0;
for (int i = 1; i <= n; i++) {
if (p) --p;
int j = sa[rk[i]-1];
while (a[i+p] == a[j+p]) ++p;
he[rk[i]] = p;
}
}
inline void ST() {//构造ST表
for (int i = 1; i <= n; i++) st[i][0] = he[i];
int w = log(n) / log(2);
for (int k = 1; k <= w; k++)
for (int i = 1; i <= n; i++) {
if (i + (1 << k) > n + 1) break;
st[i][k] = min(st[i][k-1], st[i+(1<<(k-1))][k-1]);
}
}
inline int get(int l, int r) {//求l~r之间的最小值(即l-1与r的lcp)
int k = log(r - l + 1) / log(2);
return min(st[l][k], st[r-(1<<k)+1][k]);
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
b[i] = a[i];
}
//离散化
sort(b + 1, b + n + 1);
m = unique(b + 1, b + n + 1) - (b + 1);
reverse(a + 1, a + n + 1);//倒置字符串
SA();//求sa,rk,height数组
ST();//ST表
for (int i = n; i; i--) {//倒序考虑
s.insert(rk[i]);//以rk为关键字插入set
si it = s.find(rk[i]);//找到插入的位置
int k = 0;//存最长lcp
if (it != s.begin()) {//找前驱,注意特判
int p = *(--it);
k = get(p + 1, rk[i]);
++it;
}
++it;
if (it != s.end()) {//找后继,注意特判
int p = *it;
k = max(k, get(rk[i] + 1, p));
}
ans += n + 1 - i - k;//加上新生成的子串
printf("%lld\n", ans);
}
return 0;
}
P4070 [SDOI2016]生成魔咒的更多相关文章
- bzoj4516 / P4070 [SDOI2016]生成魔咒
P4070 [SDOI2016]生成魔咒 后缀自动机 每插入一个字符,对答案的贡献为$len[last]-len[fa[last]]$ 插入字符范围过大,所以使用$map$存储. (去掉第35行就是裸 ...
- 洛谷 P4070 [SDOI2016]生成魔咒 解题报告
P4070 [SDOI2016]生成魔咒 题目描述 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 \(1\).\(2\) 拼凑起来形成一个魔咒串 \([1,2]\). 一个魔咒 ...
- Luogu P4070 [SDOI2016]生成魔咒
题目链接 \(Click\) \(Here\) 其实是看后缀数组资料看到这个题目的,但是一眼反应显然后缀自动机,每次维护添加节点后的答案贡献即可,唯一不友好的一点是需要平衡树维护,这里因为复杂度不卡而 ...
- [洛谷P4070][SDOI2016]生成魔咒
题目大意:有一个字符串,每次在末尾加入一个字符,问当前共有多少个本质不同的字串 题解:$SAM$,就是问插入这个字符后,多了多少个字串,就是当前这个点的$Right$数组大小. 卡点:无 C++ Co ...
- BZOJ4516: [Sdoi2016]生成魔咒 后缀自动机
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #inclu ...
- BZOJ 4516: [Sdoi2016]生成魔咒 [后缀自动机]
4516: [Sdoi2016]生成魔咒 题意:询问一个字符串每个前缀有多少不同的子串 做了一下SDOI2016R1D2,题好水啊随便AK 强行开map上SAM 每个状态的贡献就是\(Max(s)-M ...
- BZOJ_4516_[Sdoi2016]生成魔咒_后缀数组+ST表+splay
BZOJ_4516_[Sdoi2016]生成魔咒_后缀数组+ST表+splay Description 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 1.2 拼凑起来形成一个魔 ...
- 【LG4070】[SDOI2016]生成魔咒
[LG4070][SDOI2016]生成魔咒 题面 洛谷 题解 如果我们不用在线输的话,那么答案就是对于所有状态\(i\) \[ \sum (i.len-i.fa.len) \] 现在我们需要在线询问 ...
- [Sdoi2016]生成魔咒[SAM or SA]
4516: [Sdoi2016]生成魔咒 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1017 Solved: 569[Submit][Statu ...
随机推荐
- java中一维数组的定义和遍历
public class ArrayDemo1{ public static void main(String[] args){ //1 定义数组 并同时赋值 int[] arr = new int[ ...
- 定时任务调度-Celery
确保任务不重叠解决方法: from celery import task from celery.five import monotonic from celery.utils.log import ...
- Linux 普通用户免密码切换到root用户
Linux 普通用户免密码切换到root用户 # 添加用户 useradd user_name # 修改密码 echo "user_name@pwd" | passwd --std ...
- jenkins配置小结
启动jenkins:javaw -jar -Dhudson.model.DirectoryBrowserSupport.CSP= jenkins.war --httpPort=8001 wget h ...
- mysql5.7 on windows
1.下载zip包:https://dev.mysql.com/downloads/file/?id=476696 2.解压到E盘3.执行命令 初始化 E:/mysql-5.7.22-winx64/bi ...
- Dream team: Stacking for combining classifiers梦之队:组合分类器
sklearn实战-乳腺癌细胞数据挖掘(博主亲自录制视频) https://study.163.com/course/introduction.htm?courseId=1005269003& ...
- saltstack pillar
piller组件定义与客户端相关的任何数据(定义在master端),定义好的数据可以被其他组件调用(如state,api) 说通俗了,一句话,就是ansible vars里定义的变量,可以在整个pla ...
- Shell命令的执行顺序
shell执行命令的步骤顺序如上图,看起来有些复杂. 当命令行被处理时,每一个步骤都是在Shell的内存里发生的;Shell不会真的把每个步骤的发生显示给你看. 所以,你可以假想这事我们偷窥Shell ...
- SQL优化笔记一:索引和explain
目录 为什么需要优化SQL SQL优化的重点 索引 索引的结构 索引的优缺点总结: 索引的分类 索引操作 B树 实战 问题 数据库方面,我会使用MySQL来讲解 为什么需要优化SQL 性能低,执行时间 ...
- scrapy_splash模块解析动态js
一般遇到动态加载的网页就比较棘手,一般采用scrapy_splash和selenium这两种方式来解决.貌似scrapy_splash更强大,因为就从爬取美团这个网站而言,scrapy_splash可 ...