题目大意

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

题解

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

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

所以我们从开头插入字符

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

这样就多了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. 怎样推断 ios设备的类型(iphone,ipod,ipad)

    -(bool)checkDevice:(NSString*)name { NSString* deviceType = [UIDevice currentDevice].model; NSLog(@& ...

  2. linux 设置静态IP方法

    本系统使用 linux redhat 7.2 1.   修改ip vi   /etc/sysconfig/network-scripts/ifcfg-eno16777736 2. 修改数据项如下 3. ...

  3. 微信小程序TabBar的使用

    一.TabBar使用步骤 1.创建所需要的界面和所需要的图片: 2.配置文件: 我们找到项目根目录中的配置文件 app.json 加入如下配置信息 "tabBar": { &quo ...

  4. wifi debug command

    ==================================================================================================== ...

  5. Feature Selection 其一 —— Filter Approach

    这一个部分都将只涉及到选择特征的某个子集的方法,将高纬度的特征空间映射到低维度空间的方法(如PCA)都不会涉及到. 一. 单变量 优点:运算速度快,独立于分类器 缺点:忽略的特征之间的联系,忽略了与分 ...

  6. MySQL——函数

    MySQL数据库提供了很多函数包括: (1)数学函数 (2)字符串函数 (3)日期和时间函数 (4)条件判断函数 (5)系统信息函数 (6)加密函数 (7)格式化函数 一.数学函数 数学函数主要用于处 ...

  7. Java for LeetCode 108 Convert Sorted Array to Binary Search Tree

    Given an array where elements are sorted in ascending order, convert it to a height balanced BST. 解题 ...

  8. LeetCode:奇偶链表【328】

    LeetCode:奇偶链表[328] 题目描述 给定一个单链表,把所有的奇数节点和偶数节点分别排在一起.请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性. 请尝试使用原地 ...

  9. okhttp 特点 module版本冲突 集成module到项目

    特点适合大小文件上传.下载,但对图片不会执行缓存是一个缺点 集成module到项目 右上角添加 lib和module版本冲突问题,删除lib下的即可

  10. BZOJ3295 [Cqoi2011]动态逆序对 —— CDQ分治

    题目链接:https://vjudge.net/problem/HYSBZ-3295 3295: [Cqoi2011]动态逆序对 Time Limit: 10 Sec  Memory Limit: 1 ...