【bzoj4516】[Sdoi2016]生成魔咒 后缀数组+倍增RMQ+STL-set
题目描述
输入
输出
输出 n 行,每行一个数。第 i 行的数表示第 i 次操作后 S 的生成魔咒数量
样例输入
7
1 2 3 3 3 1 2
样例输出
1
3
6
9
12
17
22
题解
后缀自动机+map 后缀数组+set
SAM太高端了,于是我选择了后缀数组。
好在这题可以离线。
题目中求的是前缀的字串的个数,我们用的是后缀数组,于是需要先读入所有数字,离散化后倒序求sa、rank和height。
由于不重复子串的个数为为总子串个数-重复子串个数,所以我们每次加一个后缀(题目中的前缀),只需求出该串和其它串的重复部分即可。
而该后缀不包括第一个字符的所有串都一定在之前被计算过,不用考虑,只需考虑包括第一个字符的串是否重复出现过即可。
根据sa和rank的定义,挨着的串LCP(最长公共前缀)最大,那么本次计算出的重复的串的个数为之前的后缀的rank值与该后缀的rank值最接近的两个,求出它们与该后缀的LCP中较大的那个算入tmp中。
可能比较难理解。
举个例子,3 3 3 4 3 1中计算后5个以后算后6个,只需要算max(lcp(1,2),lcp(1,5))即可,因为3和33为重复的,取最大值之后是2个重复。
然后把len-tmp加入到答案中并输出即可。
求前驱后继可以使用set,求LCP可以用倍增算法RMQ,此时注意左端点需要+1。
#include <cstdio>
#include <algorithm>
#include <set>
#define N 100010
using namespace std;
struct data
{
int num , p;
}a[N];
set<int> s;
set<int>::iterator it;
int n , v[N] , m , sa[N] , r[N] , ws[N] , wa[N] , wb[N] , wv[N] , rank[N] , height[N] , log[N] , f[N][20];
bool cmp(data a , data b)
{
return a.num < b.num;
}
void da()
{
int i , j , p , *x = wa , *y = wb;
for(i = 0 ; i < m ; i ++ ) ws[i] = 0;
for(i = 0 ; i < n ; i ++ ) ws[x[i] = r[i]] ++ ;
for(i = 1 ; i < m ; i ++ ) ws[i] += ws[i - 1];
for(i = n - 1 ; i >= 0 ; i -- ) sa[--ws[x[i]]] = i;
for(p = j = 1 ; p < n ; j <<= 1 , m = p)
{
for(p = 0 , i = n - j ; i < n ; i ++ ) y[p ++ ] = i;
for(i = 0 ; i < n ; i ++ ) if(sa[i] - j >= 0) y[p ++ ] = sa[i] - j;
for(i = 0 ; i < n ; i ++ ) wv[i] = x[y[i]];
for(i = 0 ; i < m ; i ++ ) ws[i] = 0;
for(i = 0 ; i < n ; i ++ ) ws[wv[i]] ++ ;
for(i = 1 ; i < m ; i ++ ) ws[i] += ws[i - 1];
for(i = n - 1 ; i >= 0 ; i -- ) sa[--ws[wv[i]]] = y[i];
for(swap(x , y) , x[sa[0]] = 0 , p = i = 1 ; i < n ; i ++ )
{
if(y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + j] == y[sa[i] + j]) x[sa[i]] = p - 1;
else x[sa[i]] = p ++ ;
}
}
for(i = 1 ; i < n ; i ++ ) rank[sa[i]] = i;
for(p = i = 0 ; i < n - 1 ; height[rank[i ++ ]] = p)
for(p ? p -- : 0 , j = sa[rank[i] - 1] ; r[i + p] == r[j + p] ; p ++ );
}
int query(int x , int y)
{
int k = log[y - x + 1];
return min(f[x][k] , f[y - (1 << k) + 1][k]);
}
int main()
{
int i , j , tmp;
long long ans = 0;
scanf("%d" , &n);
for(i = 0 ; i < n ; i ++ ) scanf("%d" , &a[i].num) , a[i].p = i;
sort(a , a + n , cmp);
for(i = 0 ; i < n ; i ++ )
{
if(a[i].num != v[m]) v[++m] = a[i].num;
r[n - a[i].p - 1] = m;
}
n ++ , m ++ , da() , n -- ;
for(i = 2 ; i <= n ; i ++ ) log[i] = log[i >> 1] + 1;
for(i = 1 ; i <= n ; i ++ ) f[i][0] = height[i];
for(i = 1 ; i <= log[n] ; i ++ )
for(j = 1 ; j + (1 << i) - 1 <= n ; j ++ )
f[j][i] = min(f[j][i - 1] , f[j + (1 << (i - 1))][i - 1]);
for(i = n - 1 ; i >= 0 ; i -- )
{
tmp = 0;
it = s.upper_bound(rank[i]);
if(it != s.end()) tmp = max(tmp , query(rank[i] + 1 , *it));
if(it != s.begin()) tmp = max(tmp , query(*(--it) + 1 , rank[i]));
ans += (long long)n - i - tmp;
printf("%lld\n" , ans);
s.insert(rank[i]);
}
return 0;
}
【bzoj4516】[Sdoi2016]生成魔咒 后缀数组+倍增RMQ+STL-set的更多相关文章
- BZOJ4516: [Sdoi2016]生成魔咒(后缀数组 set RMQ)
题意 题目链接 Sol 毒瘤SDOI 终于有一道我会做的题啦qwq 首先,本质不同的子串的个数 $ = \frac{n(n + 1)}{2} - \sum height[i]$ 把原串翻转过来,每次就 ...
- [SDOI2016] 生成魔咒 - 后缀数组,平衡树,STL,时间倒流
[SDOI2016] 生成魔咒 Description 初态串为空,每次在末尾追加一个字符,动态维护本质不同的子串数. Solution 考虑时间倒流,并将串反转,则变为每次从开头删掉一个字符,即每次 ...
- BZOJ4516: [Sdoi2016]生成魔咒 后缀自动机
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #inclu ...
- [bzoj4516][Sdoi2016]生成魔咒——后缀自动机
Brief Description 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 1.2 拼凑起来形成一个魔咒串 [1,2]. 一个魔咒串 S 的非空字串被称为魔咒串 S 的生 ...
- BZOJ.4516.[SDOI2016]生成魔咒(后缀数组 RMQ)
题目链接 后缀自动机做法见这(超好写啊). 后缀数组是可以做的: 本质不同的字符串的个数为 \(子串个数-\sum_{ht[i]}\),即 \(\frac{n(n+1)}{2}-\sum_{ht[i] ...
- BZOJ 4516: [Sdoi2016]生成魔咒——后缀数组、并查集
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4516 题意 一开始串为空,每次往串后面加一个字符,求本质不同的子串的个数,可以离线.即长度为 ...
- BZOJ 4516: [Sdoi2016]生成魔咒(后缀数组)
传送门 解题思路 题目其实就是动态维护本质不同的串的个数.考虑到只有加数字的操作,所以可以用后缀数组.题目是每次往后加数字,这样不好处理,因为每次加数字之后所有的后缀都会改变.所以要转化一下思路,就是 ...
- BZOJ 4516: [Sdoi2016]生成魔咒 [后缀自动机]
4516: [Sdoi2016]生成魔咒 题意:询问一个字符串每个前缀有多少不同的子串 做了一下SDOI2016R1D2,题好水啊随便AK 强行开map上SAM 每个状态的贡献就是\(Max(s)-M ...
- liberOJ #2033. 「SDOI2016」生成魔咒 后缀数组
#2033. 「SDOI2016」生成魔咒 题目描述 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 1 11.2 22 拼凑起来形成一个魔咒串 [1,2] [1, 2] ...
随机推荐
- LintCode 12.带最小值操作的栈(两种方法实现)
题目描述 实现一个带有取最小值min方法的栈,min方法将返回当前栈中的最小值. 你实现的栈将支持push,pop 和 min 操作,所有操作要求都在O(1)时间内完成. 样例 如下操作:push(1 ...
- Solr6.6.0添加IK中文分词器
IK分词器就是一款中国人开发的,扩展性很好的中文分词器,它支持扩展词库,可以自己定制分词项,这对中文分词无疑是友好的. jar包下载链接:http://pan.baidu.com/s/1o85I15o ...
- Recycler实现瀑布流
(开发环境Android studio) 首先,在开发环境中添加引用(在外层的build.gradle文件下的dependencies里面添加如下引用) implementation 'com.and ...
- 修改pytorch官方实例适用于自己的二分类迁移学习项目
本demo从pytorch官方的迁移学习示例修改而来,增加了以下功能: 根据AUC来迭代最优参数: 五折交叉验证: 输出验证集错误分类图片: 输出分类报告并保存AUC结果图片. import os i ...
- 汉化manjaro下的火狐浏览器
1.下载 汉化包 sudo pacman -S firefox-i18n-zh-cn 2.查看add-ons下的language选项有没有已安装的包 3.在浏览器的地址栏输入 搜索intl.local ...
- python字符串的格式化输出
很多时候我们在打印输入内容时希望有简单格式而不是拼接 一般做法: name = input("name:").strip() age = input("age:" ...
- 【BGP的基本配置】
BGP的基本配置 一:根据项目需求搭建好拓扑图如下 二:配置 1:首先进行理论分析:RT1和RT2,3分别属于不同的AS;在RT1和RT2之间建立EBGP关系,在确保RT3可以学到RT1的8.1.1. ...
- ERROR: bootstrap checks failed
错误描述:Linux默认配置的参数过小,需要自己设置 max file descriptors [4096] for elasticsearch process is too low, increas ...
- CentOs安装Mysql和配置初始密码
mysql官网yum安装教程,地址:https://dev.mysql.com/doc/mysql-yum-repo-quick-guide/en/#repo-qg-yum-fresh-install ...
- python学习之函数基础
第一个python函数: >>> def func_1(): ... print 'hello python' ... >>> func_1() hello pyt ...