题目大意

在结尾动态插入字符,每次插入结束后输出当前串中本质不同的字串个数

题解

注意一开始是空串,然后我们我们可以打表观察规律

我们发现一直在开头插入字符和一直在结尾插入字符得到的答案是一样的

所以我们从开头插入字符

那么每次我们相于插入了一个后缀

这样就多了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: 生成魔咒 后缀数组的更多相关文章

  1. 【bzoj4516】[Sdoi2016]生成魔咒 后缀数组+倍增RMQ+STL-set

    题目描述 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 1.2 拼凑起来形成一个魔咒串 [1,2].一个魔咒串 S 的非空字串被称为魔咒串 S 的生成魔咒. 例如 S=[1,2 ...

  2. liberOJ #2033. 「SDOI2016」生成魔咒 后缀数组

    #2033. 「SDOI2016」生成魔咒     题目描述 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 1 11.2 22 拼凑起来形成一个魔咒串 [1,2] [1, 2] ...

  3. [SDOI2016] 生成魔咒 - 后缀数组,平衡树,STL,时间倒流

    [SDOI2016] 生成魔咒 Description 初态串为空,每次在末尾追加一个字符,动态维护本质不同的子串数. Solution 考虑时间倒流,并将串反转,则变为每次从开头删掉一个字符,即每次 ...

  4. BZOJ 4516: [Sdoi2016]生成魔咒——后缀数组、并查集

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4516 题意 一开始串为空,每次往串后面加一个字符,求本质不同的子串的个数,可以离线.即长度为 ...

  5. BZOJ.4516.[SDOI2016]生成魔咒(后缀数组 RMQ)

    题目链接 后缀自动机做法见这(超好写啊). 后缀数组是可以做的: 本质不同的字符串的个数为 \(子串个数-\sum_{ht[i]}\),即 \(\frac{n(n+1)}{2}-\sum_{ht[i] ...

  6. BZOJ 4516: [Sdoi2016]生成魔咒(后缀数组)

    传送门 解题思路 题目其实就是动态维护本质不同的串的个数.考虑到只有加数字的操作,所以可以用后缀数组.题目是每次往后加数字,这样不好处理,因为每次加数字之后所有的后缀都会改变.所以要转化一下思路,就是 ...

  7. BZOJ4516: [Sdoi2016]生成魔咒(后缀数组 set RMQ)

    题意 题目链接 Sol 毒瘤SDOI 终于有一道我会做的题啦qwq 首先,本质不同的子串的个数 $ = \frac{n(n + 1)}{2} - \sum height[i]$ 把原串翻转过来,每次就 ...

  8. BZOJ 4516: [Sdoi2016]生成魔咒 [后缀自动机]

    4516: [Sdoi2016]生成魔咒 题意:询问一个字符串每个前缀有多少不同的子串 做了一下SDOI2016R1D2,题好水啊随便AK 强行开map上SAM 每个状态的贡献就是\(Max(s)-M ...

  9. BZOJ4516: [Sdoi2016]生成魔咒 后缀自动机

    #include<iostream> #include<cstdio> #include<cstring> #include<queue> #inclu ...

随机推荐

  1. 数据挖掘之pandas

    sdata={'语文':89,'数学':96,'音乐':39,'英语':78,'化学':88} #字典向Series转化 @@ >>> studata=Series(sdata) & ...

  2. Oracle:复合触发器

    ----- CF_DEPTUCORGANIZATION  INSERT UPDATE DELETE 触发器CREATE  OR REPLACE TRIGGER tr_del_CF_DEPTUCORGA ...

  3. 高特权级代码段转向低特权级代码段(利用 ret(retf) 指令实现 jmp from ring0 to ring3)

    [0]写在前面 0.1)本代码旨在演示 从 ring0 转移到 ring3(即,从高特权级 转移到 低特权级) 0.2)本文 只对 与 门相关的 代码进行简要注释,言简意赅: 0.3)文末的个人总结是 ...

  4. 一个中断服务子程序ISR

    请看下面的程序(一个中断服务子程序ISR),请指出这段代码的错误.)[中国台湾某著名CPU生产公司2005年面试题] 答案:(1)ISR不能返回一个值.如果你不懂这个,那么是不会被雇用的.(2)ISR ...

  5. GS踢玩家下线功能

    GS踢玩家下线功能 //key:userId, val:nChannelId (当前在线用户) std::map<int, int> m_mapOnLineUserByUid; ///&l ...

  6. 几句话搞懂URI、URL、URN之间的关系

    1.URI,是uniform resource identifier,统一资源标识符,用来唯一的标识一个资源. 2.RL是uniform resource locator,统一资源定位器,它是一种具体 ...

  7. Asp.Net中判断是否登录,及是否有权限?

    不需要在每个页面都做判段, 方法一:只需要做以下处理即可 using System; using System.Collections.Generic; using System.Linq; usin ...

  8. HTML5画布(基础篇11-10)

    <script type="text/javascript"> $(function(){ var s = $("#myCanvas")[0]; v ...

  9. 九度OJ 1157:中位数 (中位数、排序)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:2188 解决:1294 题目描述: 中位数定义:一组数据按从小到大的顺序依次排列,处在中间位置的一个数(或最中间两个数据的平均数). 给出一 ...

  10. AWS:3. S3

    主要内容 1.S3入门 2.S3安全性 对象 权限 访问策略 3.S3实战--BAAS 应用与定价 S3入门 S3概念 S3是simple storge server简单存储服务 相当于网盘,例如百度 ...