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

时间:2022-09-07 21:55:22

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

Description

魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 1、2 拼凑起来形成一个魔咒串 [1,2]。
一个魔咒串 S 的非空字串被称为魔咒串 S 的生成魔咒。
例如 S=[1,2,1] 时,它的生成魔咒有 [1]、[2]、[1,2]、[2,1]、[1,2,1] 五种。S=[1,1,1] 时,它的生成魔咒有 [1]、
[1,1]、[1,1,1] 三种。最初 S 为空串。共进行 n 次操作,每次操作是在 S 的结尾加入一个魔咒字符。每次操作后都
需要求出,当前的魔咒串 S 共有多少种生成魔咒。

Input

第一行一个整数 n。
第二行 n 个数,第 i 个数表示第 i 次操作加入的魔咒字符。
1≤n≤100000。,用来表示魔咒字符的数字 x 满足 1≤x≤10^9

Output

输出 n 行,每行一个数。第 i 行的数表示第 i 次操作后 S 的生成魔咒数量

Sample Input

7
1 2 3 3 3 1 2

Sample Output

1
3
6
9
12
17
22

在后面添加字符每次会产生若干个后缀。于是翻转一下相当于每次只添加一个后缀。
然后如何求不同的子串个数呢?
首先对于一个后缀$Suffix(i)$,会贡献$n-i+1$个子串,但这些子串会有一些重复的,于是找$rank[i]$的前驱和后继所在的后缀设为$j$。
那么会有$Lcp(suffix(i),suffix(j))$这么多是重复的。
于是用splay维护前驱后继,然后ST表求两个后缀的LCP。
 
代码:
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define N 100050
#define ls ch[p][0]
#define rs ch[p][1]
#define get(x) (ch[f[x]][1]==x)
typedef long long ll;
int n,ws[N],wv[N],wa[N],wb[N],rank[N],sa[N],height[N],r[N];
int f[N],ch[N][2],siz[N],rt,val[N],reimu;
//////////////////////////////////////////////
struct A {
int num,id,v;
}a[N];
bool cmp1(const A &x,const A &y){return x.num<y.num;}
bool cmp2(const A &x,const A &y){return x.id<y.id;}
///////////////////////////////////////////////
void build_suffix_array() {
int i,j,p,*x=wa,*y=wb,*t,m=n;
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=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>=0) y[p++]=sa[i]-j;
for(i=0;i<n;i++) wv[i]=x[y[i]];
for(i=0;i<m;i++) ws[i]=0;
for(i=0;i<n;i++) ws[wv[i]]++;
for(i=1;i<m;i++) ws[i]+=ws[i-1];
for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
for(t=x,x=y,y=t,i=p=1,x[sa[0]]=0;i<n;i++) {
if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j]) x[sa[i]]=p-1;
else x[sa[i]]=p++;
}
}
for(i=1;i<n;i++) rank[sa[i]]=i;
for(i=p=0;i<n-1;height[rank[i++]]=p)
for(p?p--:0,j=sa[rank[i]-1];r[i+p]==r[j+p];p++);
}
/////////////////////////////////////////////
int newnode(int x) {
siz[++reimu]=1; val[reimu]=x; return reimu;
}
void pushup(int p) {
siz[p]=siz[ls]+siz[rs]+1;
}
void rotate(int x) {
int y=f[x],z=f[y],k=get(x);
ch[y][k]=ch[x][!k]; f[ch[y][k]]=y;
ch[x][!k]=y; f[y]=x; f[x]=z;
if(z) ch[z][ch[z][1]==y]=x;
pushup(y); pushup(x);
if(rt==y) rt=x;
}
void splay(int x,int y) {
for(int fa;(fa=f[x])!=y;rotate(x))
if(f[fa]!=y)
rotate(get(x)==get(fa)?fa:x);
}
void insert(int x) {
int p=rt,l,r;
while(p) {
if(val[p]>=x) r=p,p=ls;
else l=p,p=rs;
}
splay(l,0); splay(r,rt);
ch[r][0]=newnode(x);
f[reimu]=r; pushup(r); pushup(l);
}
int pre(int x) {
int p=rt,ans;
while(p) {
if(val[p]>=x) p=ls;
else ans=p,p=rs;
}
return val[ans];
}
int nxt(int x) {
int p=rt,ans;
while(p) {
if(val[p]<=x) p=rs;
else ans=p,p=ls;
}
return val[ans];
}
/////////////////////////////////////////
struct ST {
int f[N][20],L[N];
void init() {
int i,j;
memset(f,0x3f,sizeof(f));
L[1]=0;
for(i=2;i<=n;i++) L[i]=L[i>>1]+1;
for(i=0;i<=n;i++) {
f[i][0]=height[i];
}
for(j=1;(1<<j)<=n;j++) {
for(i=0;i+(1<<j)-1<=n;i++) {
f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]);
}
}
}
int get_min(int l,int r) {
int len=L[r-l+1];
return min(f[l][len],f[r-(1<<len)+1][len]);
}
}S;
int main() {
scanf("%d",&n);
int i;
for(i=1;i<=n;i++) scanf("%d",&a[n-i+1].num),a[i].id=i;
sort(a+1,a+n+1,cmp1);
int j=0;a[0].num=23333443;
for(i=1;i<=n;i++) {
if(a[i].num!=a[i-1].num) j++;
a[i].v=j;
}
sort(a+1,a+n+1,cmp2);
for(i=0;i<n;i++) {
r[i]=a[i+1].v;
}
r[n++]=0;
build_suffix_array(); /*for(i=0;i<n;i++) printf("%d ",r[i]); puts("");
for(i=0;i<n;i++) printf("%d ",sa[i]); puts("");
for(i=0;i<n;i++) printf("%d ",height[i]); puts("");
for(i=0;i<n;i++) printf("%d ",rank[i]);puts("");*/ ll ans=0;
rt=newnode(-100000000);
ch[rt][1]=newnode(100000000);
f[ch[rt][1]]=rt;
pushup(rt); S.init();
for(i=n-2;i>=0;i--) {
ans+=n-i-1;
int pr=pre(rank[i]);
int tmp=0;
if(pr>=0) {
tmp=S.get_min(pr+1,rank[i]);
}
int nx=nxt(rank[i]);
if(nx<=n) {
tmp=max(tmp,S.get_min(rank[i]+1,nx));
}
ans-=tmp;
insert(rank[i]);
printf("%lld\n",ans);
}
}
/*
7
1 2 3 3 3 1 2
*/

BZOJ_4516_[Sdoi2016]生成魔咒_后缀数组+ST表+splay的更多相关文章

  1. bzoj千题计划283:bzoj4516&colon; &lbrack;Sdoi2016&rsqb;生成魔咒(后缀数组)

    http://www.lydsy.com/JudgeOnline/problem.php?id=4516 考虑在后面新加一个字母产生的影响 假设是第i个 如果不考虑重复,那么会增加i个不同的字符串 考 ...

  2. BZOJ4516 SDOI2016生成魔咒(后缀数组&plus;平衡树)

    一个字符串本质不同的子串数量显然是总子串数减去所有height值.如果一个个往里加字符的话,每次都会改动所有后缀完全没法做.但发现如果从后往前加的话,每次只会添加一个后缀.于是我们把字符串倒过来,每次 ...

  3. BZOJ4516 &lbrack;Sdoi2016&rsqb;生成魔咒 【后缀自动机】

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

  4. cogs2223&period; &lbrack;SDOI2016 Round1&rsqb; 生成魔咒(后缀数组 hash 二分 set

    题意:对一个空串每次在后面加一个字符,问每加完一次得到的字符串有几个不同的子串. 思路:每个子串都是某个后缀的前缀,对于每个后缀求出他能贡献出之前没有出现过的前缀的个数,答案累加就行. 要求每个后缀的 ...

  5. 2018&period;12&period;23 bzoj4516&colon; &lbrack;Sdoi2016&rsqb;生成魔咒(后缀自动机)

    传送门 samsamsam入门题. 题意简述:给出一个串让你依次插入字符,求每次插入字符之后不同子串的数量. 显然每次的变化量只跟新出现的nnn个后缀有关系,那么显然就是maxlenp−maxlenl ...

  6. &lbrack;SDOI2016&rsqb;生成魔咒(后缀自动机)

    看一眼题.本质不同的字串数. 嘴角微微上扬. 每一次加一个数输出一个答案. 笑容渐渐消失. 等等,\(SAM\)好像也可以求本质不同的字串. 设当前字符串用\(x\)表示,每次插入完成后\(ans\) ...

  7. 【洛谷 P4070】 &lbrack;SDOI2016&rsqb;生成魔咒(后缀自动机)

    题目链接 建出\(SAM\)后,不同子串个数就是\(\sum len(i)-len(fa(i))\) 因为\(SAM\)在线的,所以每加入一个字符就能直接加上其贡献,于是这道题就没了. 因为\(x\) ...

  8. BZOJ4516 SDOI2016生成魔咒(后缀自动机)

    本质不同子串数量等于所有点的len-parent树上父亲的len的和.可以直接维护. #include<iostream> #include<cstdio> #include& ...

  9. &lbrack;SDOI2016&rsqb; 生成魔咒 - 后缀数组&comma;平衡树&comma;STL&comma;时间倒流

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

随机推荐

  1. Mac Mail PGP Setup 如何在苹果电脑上设置安全邮件 良好隐私密码法(英语:Pretty Good Privacy,缩写为PGP)

    背景知识 良好隐私密码法(英语:Pretty Good Privacy,缩写为PGP),一套用于讯息加密.验证的应用程序,采用IDEA的散列算法作为加密与验证之用. 关联文献:https://en.w ...

  2. 数据库imp导表dmp的方法

    1>sqlplus / as sysdba 进入sqlplus 2>drop user USER cascade 3>create user USER IDENTIFIED BY P ...

  3. Android设计模式系列

    http://www.cnblogs.com/qianxudetianxia/category/312863.html Android设计模式系列(12)--SDK源码之生成器模式(建造者模式) 摘要 ...

  4. 四句话总结JavaScript作用域

    上一篇文章中简单介绍了一下JS作用域,本篇将作进一步探究和总结. 前言:JavaScript的作用域一直以来都是前端开发中比较难以理解的知识点,JavaScript6中新引入了 let 关键字,用于指 ...

  5. 【百度地图API】你看过房产地图吗?你知道房产标注是如何建立的吗?

    原文:[百度地图API]你看过房产地图吗?你知道房产标注是如何建立的吗? 你是不是看过很多房产网站?例如安居客,新浪乐居. 你是不是也想做一个能写文字的标注? 你知道怎么去实现麼? 其实,上图这样的标 ...

  6. MyBatis笔记----报错:Error creating bean with name &&num;39&semi;sqlSessionFactory&&num;39&semi; defined in class path resource &lbrack;com&sol;ij34&sol;mybatis&sol;applicationContext&period;xml&rsqb;&colon; Invocation of init method failed&semi; nested exception is org&period;sp

    四月 05, 2017 4:51:02 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRef ...

  7. linux 安装Brew

    点击查看原文 Linuxbrew:Linux下的Homebrew amendgit 关注 2017.02.16 17:20* 字数 455 阅读 4745评论 0喜欢 2 前不久还在跟同事抱怨ubun ...

  8. MySQL之更新型触发器

    DELIMITER || CREATE TRIGGER tri_video_class AFTER UPDATE ON 数据库名.表名称 FOR EACH ROW begin IF((old.stat ...

  9. Mac 10&period;12使用free命令(fish)

    前提:要使用free命令,可以安装fish shell,然后在里面安装free插件. 安装fish brew install fish 安装free fish fisher free 使用 fish ...

  10. 浅谈nodejs中HTTP模块应用

    这里给大家分享下后端人员如果利用nodejs对数据的一些处理情况  适用于初学者使用 大牛勿喷 给大家分享下主要后端思想部分代码,前端部分就不展示了 const http = require(&quo ...