Portal --> loj2033

Solution

  这题。。虽然说好像也是sam的裸题不过既然在智力康复那就强制后缀数组吧qwq

​  (晚点再用sam写一次qwq)

  首先如果是要求本质不同的串的数量的话,如果说只用求一次,那么我们直接跑出这个串的\(Sa\)和\(height\),然后直接对于每一位\(i\)将\(ans+=(n-sa[i]+1)-height[i]\),具体的话可以看这里Portal-->

​  然后现在的问题是。。每加入一个字符我们都需要这么求一次

​  那么我们可以考虑将这个串反过来,这样往末端加字符的操作就变成了往前段加字符,也就是变成了多加一个后缀,这样我们就可以比较好处理这个问题了

​  我们先对原串的反串求出\(Sa\)和\(height\),然后用一个set或者。。额其实链表也是可以的来维护当前有哪些后缀(存\(rk\)值就好了),顺序按照这些后缀的\(rk\)值来排,每次我们找到插入当前这个后缀的位置,记加入这个后缀之后,前面的那个位置的\(rk\)值为\(pre\),后面那个位置的\(rk\)值为\(nxt\),那么我们就将原来的贡献(也就是\((n-sa[nxt]+1)-lcp(pre,nxt)\))减去,然后将新的贡献也就是当前后缀和\(pre\)的贡献、当前后缀和\(nxt\)的贡献加上,一直这么操作就好了

​  需要注意的是,这里要用long long,并且一开始的时候\(set\)中应该有一个\(0\),这样才能将第一个后缀的贡献算进去,以及因为数字\(x\)的范围有点吓人所以我们需要离散化一下

  

  代码大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#define ll long long
using namespace std;
const int N=1e5+10,TOP=20,inf=2147483647;
int s[N],lis[N];
ll ans[N];
int n;
namespace Sa{/*{{{*/
set<int> rec;
set<int>::iterator it,pre,nxt;
int a[N],b[N],c[N],sa[N],height[N],rk[N];
int mn[N][TOP+1];
int tot,mx,n;
bool cmp(int x,int y,int len,int *r)
{return r[x]==r[y]&&r[x+len]==r[y+len];}
void sort(int n){
for (int i=0;i<=mx;++i) c[i]=0;
for (int i=1;i<=n;++i) ++c[a[b[i]]];
for (int i=1;i<=mx;++i) c[i]+=c[i-1];
for (int i=n;i>=1;--i) sa[c[a[b[i]]]--]=b[i];
}
void get_sa(int _n){
n=_n;
int cnt=0;
mx=0;
for (int i=1;i<=n;++i) a[i]=s[i],b[i]=i,mx=max(a[i],mx);
sort(n);
for (int len=1;cnt<n;len<<=1){
cnt=0;
for (int i=n-len+1;i<=n;++i) b[++cnt]=i;
for (int i=1;i<=n;++i)
if (sa[i]>len)
b[++cnt]=sa[i]-len;
sort(n);
swap(a,b);
cnt=1; a[sa[1]]=1;
for (int i=2;i<=n;a[sa[i++]]=cnt)
if (!cmp(sa[i],sa[i-1],len,b)) ++cnt;
mx=cnt;
}
}
void rmq(){
for (int i=1;i<=n;++i) mn[i][0]=height[i];
for (int j=1;j<=TOP;++j)
for (int i=n-(1<<j)+1;i>=1;--i)
mn[i][j]=min(mn[i][j-1],mn[i+(1<<j-1)][j-1]);
}
void get_height(){
for (int i=1;i<=n;++i) rk[sa[i]]=i;
int k=0;
for (int i=1;i<=n;++i){
if (k) --k;
while (s[i+k]==s[sa[rk[i]-1]+k]) ++k;
height[rk[i]]=k;
}
rmq();
}
int lcp(int x,int y){//x,y are ranks
if (x==y) return n-sa[x]+1;
if (x>y) swap(x,y);
++x;
int len=y-x+1,lg=(int)(log(1.0*len)/log(2.0));
return min(mn[x][lg],mn[y-(1<<lg)+1][lg]);
}
int get_val(int l,int r){
if (l>r) swap(l,r);
return (n-sa[r]+1)-lcp(l,r);
}
int calc(){
int ret=0;
for (int i=1;i<=n;++i)
ret+=(n-sa[i]+1)-height[i];
return ret;
}
void print(){
set<int>::iterator tmp;
for (tmp=rec.begin();tmp!=rec.end(); ++tmp)
printf("%d ",*tmp);
printf("\n");
}
void solve(int n){
rec.clear();
rec.insert(0); rec.insert(inf); ans[n+1]=0;
//print();
for (int i=n;i>=1;--i){
it=rec.insert(rk[i]).first; pre=it; nxt=it;
--pre; ++nxt;
ans[i]=ans[i+1];
if (*nxt!=inf){
ans[i]-=get_val(*pre,*nxt);
ans[i]+=get_val(*it,*nxt);
}
ans[i]+=get_val(*pre,*it);
//print();
}
}
}/*}}}*/
void prework(){
sort(lis+1,lis+1+n);
lis[0]=unique(lis+1,lis+1+n)-lis-1;
for (int i=1;i<=n;++i) s[i]=lower_bound(lis+1,lis+1+lis[0],s[i])-lis;
} int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%d",s+i),lis[i]=s[i];
prework();
reverse(s+1,s+1+n);
Sa::get_sa(n);
Sa::get_height();
Sa::solve(n);
for (int i=n;i>=1;--i) printf("%lld\n",ans[i]);
}

【loj2033】生成魔咒的更多相关文章

  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. [SDOI2016]生成魔咒

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

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

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

  5. [BZOJ 4516] [SDOI 2016] 生成魔咒

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

  6. P4070 [SDOI2016]生成魔咒

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

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

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

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

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

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

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

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

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

随机推荐

  1. Siki_Unity_2-4_UGUI_Unity5.1 UI 案例学习

    Unity 2-4 UGUI Unity5.1 UI 案例学习 任务1-1:UGUI简介 什么是GUI: 游戏的开始菜单 RPG游戏的菜单栏.侧边栏和功能栏(比如背包系统.任务列表等) 设计用来控制移 ...

  2. String、StringBuffer、StringBuilder有什么区别

    区别 先说说String和StringBuffer/StringBuilder: String是标准的不可变类,是一个字符串常量池,并且声明的对象在方法中是唯一存在的. StringBuffer/St ...

  3. mysql5.5 升级到 5.7 的坑

    1.大概思路,docker 新启一个mysql5.7 端口映射到3307 2. 导出5.5 的.sql文件,导入5.7中 3.测试通过后,可将5.5关闭.5.7端口改回3306 GRANT ALL P ...

  4. 最大公共子串:DP

    标题:最大公共子串 最大公共子串长度问题就是:求两个串的所有子串中能够匹配上的最大长度是多少. 比如:"abcdkkk" 和 "baabcdadabc",可以找 ...

  5. Java:有关自定数组的学习

    Java:有关==自定数组==的学习 在 ==<Java程序设计与数据结构教程>== 里我在==P212~P213==页看到一个GradeRange的程序,它用的数组是自定设定的Grade ...

  6. java 数据库查询Date类型字段 没有了时分秒 全为 00 的解决办法

    当取出这个值的时候只能用java.sql.Date而且只能显示年月日,我想知道如何才能显示时分秒   PS:不改变用getdate()存入的前提下! 解决方法:将getDate()方法改为getTim ...

  7. 如何防止app接口被别人调用

    app开发的时候,如何保护app的接口呢? 用https是我想到的办法,但是不知道怎么实现,所以就考虑用token,虽然不是绝对有效,但是能防止一般的用户来攻击,高手非要攻击,只能报警了吧. toke ...

  8. Java中的网络编程-3

    用户数据协议(UDP)是网络信息传输的另外一种形式, 基于UDP的通信不同于基于TCP的通信, 基于UDP的信息传递更快, 但是不提供可靠的保证. 使用UDP传输数据时, 用户无法知道数据能否正确地到 ...

  9. Learn Docker(一)—软件安装与常规操作

    一.安装Docker Windows平台 在Windows10 X64专业版上可以直接下载Docker原生应用进行安装,在控制面板的程序与功能里启用Hyper-v,之后就可以运行docker程序啦. ...

  10. Apache 的知识点

    apache 的官方文档 http://httpd.apache.org/docs/ Mac下如何查看Apache的版本 在终端(Terminal)中输入 apachectl -v,之后回车,结果如下 ...