题目大意

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

题解

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

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

所以我们从开头插入字符

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

这样就多了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. PHP网站常见安全漏洞 及相应防范措施总结

    一.常见PHP网站安全漏洞 对于PHP的漏洞,目前常见的漏洞有五种.分别是Session文件漏洞.SQL注入漏洞.脚本命令执行漏洞.全局变量漏洞和文件漏洞.这里分别对这些漏洞进行简要的介绍. 1.se ...

  2. hdu 1068 Girls and Boys 二分图的最大匹配

    题目链接:pid=1068">http://acm.hdu.edu.cn/showproblem.php? pid=1068 #include <iostream> #in ...

  3. C语言内存分配函数malloc——————【Badboy】

    C语言中经常使用的内存分配函数有malloc.calloc和realloc等三个,当中.最经常使用的肯定是malloc,这里简单说一下这三者的差别和联系. 1.声明 这三个函数都在stdlib.h库文 ...

  4. 在linux系统中I/O 调度的选择 (转)

    I/O 调度算法再各个进程竞争磁盘I/O的时候担当了裁判的角色.他要求请求的次序和时机做最优化的处理,以求得尽可能最好的整体I/O性能. 在linux下面列出4种调度算法 CFQ (Completel ...

  5. Double类parseDouble()和valueOf()方法的区别

    数字类型的String字符串转换为浮点数通常采用parseDouble()和valueOf()方法, 两者主要是存在以下两点区别. 区别一:参数区别Double.parseDouble(java.la ...

  6. 创业做移动互联网App的4个注意事项

    导语:大多数人对于做App还是比較盲目,有个想法立刻就去做了.做出来了才忽然想到市场和推广.我把做移动 互联网App注意事项情给大家列下. 文| 移动互联网李建华 近 来,常常有人问我关于推广的事情, ...

  7. hadoop 相关工具访问端口(转)

    原文:http://www.tuicool.com/articles/BB3eArJ hadoop系统部署时用到不少端口.有的是Web UI所使用的,有的是内部通信所使用的,有的是监控所使用的.实际系 ...

  8. 爬虫-【selenium——webElement常用方法】

    a)clear——清除元素的内容 driver.find_element_by_id("**").clesr() b)send_keys——在元素上模拟按键输入 driver.fi ...

  9. linux复制和移动

    复制: -f  强制覆盖同名文件 -r  按递归方式保留原目录结构复制文件 cp -Rf /home/user1/*   /root/temp/ 将/home/user1目录下的所有东西拷到/root ...

  10. css三角形实现的几种方法的区别

    演变: .triangle{ height: 30px; width: 30px; display: inline-block; border: 30px solid; border-color: # ...