这是一道\(SA\)的练手好题

建议做之前先去做一下2408

之后你就肯定会做这道题了

首先上面那道题的答案就是

\[\sum_{i=1}^nn+1-sa[i]-het[i]
\]

就是对于每一个后缀求出其能产生的子串,之后减掉和之前本质相同的子串

对于这个题,我们需要求出所有前缀的本质不同的子串个数

先无脑敲上\(sa\)和\(het\)的板子,之后我们只需要往里面动态添加后缀就好了

但是如果正着处理的话会有一个非常显然的问题,也就是我们加进去一个后缀,但是这个后缀和之前的一些后缀形成的\(lcp\)长度超过当前的长度,会导致我们很难计算

所以我们需要把字符串倒过来,之后每次往里面添加一个后缀就只相当于往里面添加了一个字符

反置字符串显然不会令子串变得不相等,于是我们可以完美解决这个问题

之后我们维护上面的那个柿子就好了,由于我们插入的\(sa\)值并不连续,所以我们不能直接用\(het\),而是\(het\)的最小值

于是我们用一个\(st\)表来查询\(het\)的最小值,之后每插入一个点相当于要断裂一个原来存在的排名连续的后缀,所以还需要一个\(set\)来找前驱和后继

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<set>
#define re register
#define LL long long
#define maxn 100005
#define set_it std::set<int>::iterator
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read()
{
re char c=getchar();int x=0;
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
int a[maxn],rk[maxn],tp[maxn],tax[maxn],sa[maxn],het[maxn],b[maxn],to[maxn];
int St[maxn][18],log_2[maxn];
int n,m,sz;LL ans=1;
std::set<int> s;
inline void qsort()
{
for(re int i=0;i<=m;i++) tax[i]=0;
for(re int i=1;i<=n;i++) tax[rk[i]]++;
for(re int i=1;i<=m;i++) tax[i]+=tax[i-1];
for(re int i=n;i;--i) sa[tax[rk[tp[i]]]--]=tp[i];
}
inline int Pre(int x)
{
s.insert(x); set_it i=s.find(x);
if(i==s.begin()) return -1; --i; return *i;
}
inline int Nxt(int x) {set_it i=s.find(x);++i;if(i==s.end()) return -1;return *i;}
inline int find(int x)
{
int l=1,r=sz;while(l<=r)
{
int mid=l+r>>1;if(b[mid]==x) return mid;
if(b[mid]<x) l=mid+1;else r=mid-1;
}return 0;
}
inline int ask(int l,int r) {int k=log_2[r-l+1];return min(St[l][k],St[r-(1<<k)+1][k]);}
int main()
{
n=read();for(re int i=n;i;--i) a[i]=read(),b[i]=a[i];
std::sort(b+1,b+n+1);m=sz=std::unique(b+1,b+n+1)-b-1;
for(re int i=1;i<=n;i++) a[i]=find(a[i]);
for(re int i=1;i<=n;i++) rk[i]=a[i],tp[i]=i;
qsort();
for(re int w=1,p=0;p<n;w<<=1,m=p)
{
p=0;
for(re int i=1;i<=w;i++) tp[++p]=n-w+i;
for(re int i=1;i<=n;i++) if(sa[i]>w) tp[++p]=sa[i]-w;
qsort();for(re int i=1;i<=n;i++) std::swap(rk[i],tp[i]);
rk[sa[1]]=p=1;
for(re int i=2;i<=n;i++) rk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p;
}
int k=0;
for(re int i=1;i<=n;i++)
{
if(k) --k;
int j=sa[rk[i]-1];
while(a[i+k]==a[j+k]) ++k;
het[rk[i]]=k;
}
for(re int i=2;i<=n;i++) log_2[i]=1+log_2[i>>1];
for(re int i=1;i<=n;i++) St[i][0]=het[i];
for(re int j=1;j<=17;j++)
for(re int i=1;i+(1<<j)-1<=n;i++)
St[i][j]=min(St[i][j-1],St[i+(1<<(j-1))][j-1]);puts("1");s.insert(rk[n]);
for(re int i=n-1;i;--i)
{
ans+=n-i+1;
int x=Pre(rk[i]);
if(x!=-1) {int t=ask(x+1,rk[i]);ans+=to[x],ans-=t;to[x]=t;}
x=Nxt(rk[i]);
if(x!=-1) to[rk[i]]=ask(rk[i]+1,x),ans-=to[rk[i]];
printf("%lld\n",ans);
}
return 0;
}

【[SDOI2016]生成魔咒】的更多相关文章

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

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

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

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

  3. BZOJ_4516_[Sdoi2016]生成魔咒_后缀数组+ST表+splay

    BZOJ_4516_[Sdoi2016]生成魔咒_后缀数组+ST表+splay Description 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 1.2 拼凑起来形成一个魔 ...

  4. P4070 [SDOI2016]生成魔咒

    题目地址:P4070 [SDOI2016]生成魔咒 相信看到题目之后很多人跟我的思路是一样的-- 肯定要用 SA(P3809 [模板]后缀排序) 肯定要会求本质不同的子串个数(P2408 不同子串个数 ...

  5. bzoj4516 / P4070 [SDOI2016]生成魔咒

    P4070 [SDOI2016]生成魔咒 后缀自动机 每插入一个字符,对答案的贡献为$len[last]-len[fa[last]]$ 插入字符范围过大,所以使用$map$存储. (去掉第35行就是裸 ...

  6. 【LG4070】[SDOI2016]生成魔咒

    [LG4070][SDOI2016]生成魔咒 题面 洛谷 题解 如果我们不用在线输的话,那么答案就是对于所有状态\(i\) \[ \sum (i.len-i.fa.len) \] 现在我们需要在线询问 ...

  7. 洛谷 P4070 [SDOI2016]生成魔咒 解题报告

    P4070 [SDOI2016]生成魔咒 题目描述 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 \(1\).\(2\) 拼凑起来形成一个魔咒串 \([1,2]\). 一个魔咒 ...

  8. [Sdoi2016]生成魔咒[SAM or SA]

    4516: [Sdoi2016]生成魔咒 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1017  Solved: 569[Submit][Statu ...

  9. 4516: [Sdoi2016]生成魔咒

    4516: [Sdoi2016]生成魔咒 链接 题意: 求本质不同的子串. 分析: 后缀数组或者SAM都可以. 考虑SAM中每个点的可以表示的子串是一个区间min(S)~max(S),把每个点的这个区 ...

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

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

随机推荐

  1. thinkphp 查询单个“年-月-日” FROM_UNIXTIME

    *时间格式转换,使datetimepicker.js 能够搜索 查询 单个 年月日而不需是区间 查询. //另起一行看转换结果 select *,FROM_UNIXTIME(CreateTime,'% ...

  2. C#基础笔记(第十三天)

    1.复习泛型集合List<T>Dictionary<Tkey,Tvalue>装箱和拆箱装箱:把值类型转换为引用类型拆箱:把引用类型转换为值类型 我们应该尽量避免在代码中发生装箱 ...

  3. js-权威指南学习笔记13

    第十三章 Web浏览器中的JavaScript 1.在客户端JS中,window对象也是全局对象. 2.window对象中其中一个最重要的属性是document,它引用Document对象. 3.JS ...

  4. 小程序视图层(xx.xml)和逻辑层(xx.js)

    整个系统分为两块视图层(View)和逻辑层(App Service) 框架可以让数据与视图非常简单地保持同步.当做数据修改的时候,只需要在逻辑层修改数据,视图层就会做相应的更新. 通过这个简单的例子来 ...

  5. Docker for Windows(二)登录与配置镜像加速器

    一.启动Docker for Windows 通过桌面Docker Desktop启动,右下角出现的白色鲸鱼图标保持稳定时,表示Docker正在正常运行,将鼠标移到图标上会显示"Docker ...

  6. thinkPHP3.2.2 数据库对表的操作

    增加:M('表名')->add($data); 删除:M('表名')->delete($id); 更新:M('表名')->save($data); 查询:M('表名')->se ...

  7. input文字垂直居中和按钮对齐问题,兼容IE8

    1.盒子模型问题:请CSS重置 2.按钮不对齐:请浮动或者vertical-align:middle;然后计算宽高,使其对齐 : 3.IE8文本不居中:line-height属性     注意:IE8 ...

  8. 如何解决css-子div设置margin-top后,父div与子div一起下移的bug?

    根据规范,一个盒子如果没有上补白(padding-top)和上边框(border-top),那么这个盒子的上边距会和其内部文档流中的第一个子元素的上边距重叠. 这是规范引起的普遍问题. 只要给父盒子设 ...

  9. this and super

    this 和 super 的区别:this, 先从本类找属性和方法,本类找不到再从父类找.super, 从父类找. this 和 super 都可以调用构造方法,所以this() 和 super() ...

  10. PRINCE2是什么?

    PRINCE是PRoject IN Controlled Environment(受控环境下的项目管理)的简称. PRINCE2描述了如何以一种逻辑性的.有组织的方法,按照明确的步骤对项目进行管理. ...