bzoj 4516: 生成魔咒 后缀数组
题目大意
在结尾动态插入字符,每次插入结束后输出当前串中本质不同的字串个数
题解
注意一开始是空串,然后我们我们可以打表观察规律
我们发现一直在开头插入字符和一直在结尾插入字符得到的答案是一样的
所以我们从开头插入字符
那么每次我们相于插入了一个后缀
这样就多了n-sa[i]个前缀
但是这些前缀中有重复的
所以我们要在已经插入的后缀中找出与之最长的lcp长度
减去这个长度就是我们得到的不同的字串个数了
由于求lcp时是对height一直取min
所以我们找最长的lcp是只需要找所有已经计算的了后缀中,rank最接近当前加入的后缀的rank的两个后缀即可
复杂度\(O(nlogn)\)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(ll &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
inline ll cat_max(const ll &a,const ll &b){return a>b ? a:b;}
inline ll cat_min(const ll &a,const ll &b){return a<b ? a:b;}
const ll maxn = 100010;
ll wa[maxn],wb[maxn],ws[maxn];
ll rank[maxn],height[maxn],sa[maxn];
inline bool cmp(ll *r,ll i,ll j,ll k){
return r[i] == r[j] && r[i+k] == r[j+k];
}
void da(ll *r,ll n,ll m){
ll 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(j=1,p=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) y[p++] = sa[i] - j;
for(i=0;i<m;++i) ws[i] = 0;
for(i=0;i<n;++i) ws[x[y[i]]]++;
for(i=1;i<m;++i) ws[i] += ws[i-1];
for(i=n-1;i>=0;--i) sa[--ws[x[y[i]]]] = y[i];
for(swap(x,y),p=1,i=1,x[sa[0]] = 0;i<n;++i)
x[sa[i]] = cmp(y,sa[i-1],sa[i],j) ? p-1 : p++;
}
}
inline void get_h(ll *r,ll n){
ll i,j,k=0;for(i=1;i<=n;++i) rank[sa[i]] = i;
for(i=0;i<n;height[rank[i++]] = k)
for(k ? --k : 0,j = sa[rank[i]-1];r[i+k] == r[j+k];++k);
}
ll loger[maxn],minn[maxn][22],n;
inline void pre(){
loger[1] = 0;
for(ll i=2;i<=n;++i){
loger[i] = loger[i-1];
if( (1 << loger[i]+1) == i) ++loger[i];
}
for(ll i=n;i>=1;--i){
minn[i][0] = height[i];
for(ll j=1;i+(1<<j)-1<=n;++j){
minn[i][j] = min(minn[i][j-1],minn[i+(1<<j-1)][j-1]);
}
}
}
inline ll lcp(ll x,ll y){
if(x+1 > y) swap(x,y);++x;
ll k = loger[y-x+1];
return min(minn[x][k],minn[y-(1<<k)+1][k]);
}
ll a[maxn],b[maxn];
ll pos_min[maxn<<2],pos_max[maxn<<2],M;
inline void modify(ll x,ll y){
for(pos_min[x+=M]=y,pos_max[x]=y,x>>=1;x;x>>=1){
pos_min[x] = min(pos_min[x<<1],pos_min[x<<1|1]);
pos_max[x] = max(pos_max[x<<1],pos_max[x<<1|1]);
}
}
inline ll query_min(ll s,ll t){
if(s > t) return -1;
ll ret = pos_min[0];
for(s+=M-1,t+=M+1;s^t^1;s>>=1,t>>=1){
if(~s&1) ret = min(ret,pos_min[s^1]);
if( t&1) ret = min(ret,pos_min[t^1]);
}return ret == pos_min[0] ? -1 : ret;
}
inline ll query_max(ll s,ll t){
if(s > t) return -1;
ll ret = pos_max[0];
for(s+=M-1,t+=M+1;s^t^1;s>>=1,t>>=1){
if(~s&1) ret = max(ret,pos_max[s^1]);
if( t&1) ret = max(ret,pos_max[t^1]);
}return ret == pos_max[0] ? -1 : ret;
}
int main(){
read(n);for(M=1;M<(n+1);M<<=1);
memset(pos_min, 0x3f,sizeof pos_min);
memset(pos_max,-0x3f,sizeof pos_max);
for(ll i=0;i<n;++i){
read(a[n-i-1]);b[i] = a[n-i-1];
}
sort(b,b+n);
for(ll i=0;i<n;++i){
a[i] = lower_bound(b,b+n,a[i]) - b + 1;
}a[n] = 0;
da(a,n+1,n+1);get_h(a,n);pre();
ll ans = 0;
for(ll i=n-1;i>=0;--i){
ans += n-i;
ll x = query_max(1,rank[i]-1);
ll y = query_min(rank[i]+1,n);
if(x != -1 && y != -1) ans -= max(lcp(rank[i],x),lcp(rank[i],y));
else if(x == -1 && y != -1) ans -= lcp(rank[i],y);
else if(x != -1 && y == -1) ans -= lcp(rank[i],x);
printf("%lld\n",ans);
modify(rank[i],rank[i]);
}
getchar();getchar();
return 0;
}
bzoj 4516: 生成魔咒 后缀数组的更多相关文章
- 【bzoj4516】[Sdoi2016]生成魔咒 后缀数组+倍增RMQ+STL-set
题目描述 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 1.2 拼凑起来形成一个魔咒串 [1,2].一个魔咒串 S 的非空字串被称为魔咒串 S 的生成魔咒. 例如 S=[1,2 ...
- liberOJ #2033. 「SDOI2016」生成魔咒 后缀数组
#2033. 「SDOI2016」生成魔咒 题目描述 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 1 11.2 22 拼凑起来形成一个魔咒串 [1,2] [1, 2] ...
- [SDOI2016] 生成魔咒 - 后缀数组,平衡树,STL,时间倒流
[SDOI2016] 生成魔咒 Description 初态串为空,每次在末尾追加一个字符,动态维护本质不同的子串数. Solution 考虑时间倒流,并将串反转,则变为每次从开头删掉一个字符,即每次 ...
- BZOJ 4516: [Sdoi2016]生成魔咒——后缀数组、并查集
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4516 题意 一开始串为空,每次往串后面加一个字符,求本质不同的子串的个数,可以离线.即长度为 ...
- BZOJ.4516.[SDOI2016]生成魔咒(后缀数组 RMQ)
题目链接 后缀自动机做法见这(超好写啊). 后缀数组是可以做的: 本质不同的字符串的个数为 \(子串个数-\sum_{ht[i]}\),即 \(\frac{n(n+1)}{2}-\sum_{ht[i] ...
- BZOJ 4516: [Sdoi2016]生成魔咒(后缀数组)
传送门 解题思路 题目其实就是动态维护本质不同的串的个数.考虑到只有加数字的操作,所以可以用后缀数组.题目是每次往后加数字,这样不好处理,因为每次加数字之后所有的后缀都会改变.所以要转化一下思路,就是 ...
- BZOJ4516: [Sdoi2016]生成魔咒(后缀数组 set RMQ)
题意 题目链接 Sol 毒瘤SDOI 终于有一道我会做的题啦qwq 首先,本质不同的子串的个数 $ = \frac{n(n + 1)}{2} - \sum height[i]$ 把原串翻转过来,每次就 ...
- BZOJ 4516: [Sdoi2016]生成魔咒 [后缀自动机]
4516: [Sdoi2016]生成魔咒 题意:询问一个字符串每个前缀有多少不同的子串 做了一下SDOI2016R1D2,题好水啊随便AK 强行开map上SAM 每个状态的贡献就是\(Max(s)-M ...
- BZOJ4516: [Sdoi2016]生成魔咒 后缀自动机
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #inclu ...
随机推荐
- cocos2dx中使用iconv转码(win32,iOS,Android)
首先贴下环境:Win7 64, NDK r8e, libiconv-1.14, cygwin 一 Win32环境配置 Cocos2D-X自带有win32上的iconv库.仅仅须要配置一下就可以使用. ...
- SQL Server中排名函数row_number,rank,dense_rank,ntile详解
SQL Server中排名函数row_number,rank,dense_rank,ntile详解 从SQL SERVER2005开始,SQL SERVER新增了四个排名函数,分别如下:1.row_n ...
- ejabberd日志分析客户端登录流程
通过ejabberd的日志,整理了下客户端登录流程. 1. 通过TCP连接5222端口的流程: (1) 客户端向服务器发送stream流 <stream:stream to="nba. ...
- Android 繪圖白板元件,有畫筆和板擦的功能 (转)
package com.example.drawboard; import java.util.ArrayList; import java.util.List; import android.con ...
- java jdbc 同时操作查询删除操作
Connection conn = null; try { // 创建连接实例 conn = JdbcUtility.GetFactory() ...
- 【BZOJ1097】[POI2007]旅游景点atr 最短路+状压DP
[BZOJ1097][POI2007]旅游景点atr Description FGD想从成都去上海旅游.在旅途中他希望经过一些城市并在那里欣赏风景,品尝风味小吃或者做其他的有趣的事情.经过这些城市的顺 ...
- 【BZOJ2476】战场的数目 矩阵乘法
[BZOJ2476]战场的数目 Description Input 输入文件最多包含25组测试数据,每个数据仅包含一行,有一个整数p(1<=p<=109),表示战场的图形周长.p=0表示输 ...
- 高复用率的RTSPClient组件EasyRTSPClient调用说明
EasyRTSPClient 调用说明 概述 EasyRtspClient是EasyDarwin家族中针对RTSP协议的拉流组件 EasyRtspClient视频支持H264.H265.MJPEG格式 ...
- 使用@Scheduled注解编写spring定时任务
import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframewor ...
- maven 配置: 修改默认的 .m2仓库 默认存储路径.
maven 配置: 修改默认的 .m2仓库 默认存储路径. 一 .在系统maven里修改 1.在maven_HOME/conf/下找到配置文档 settings.xml 在文档中添加如下的配置说明 & ...