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。
 
代码:
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <algorithm>
  4. using namespace std;
  5. #define N 100050
  6. #define ls ch[p][0]
  7. #define rs ch[p][1]
  8. #define get(x) (ch[f[x]][1]==x)
  9. typedef long long ll;
  10. int n,ws[N],wv[N],wa[N],wb[N],rank[N],sa[N],height[N],r[N];
  11. int f[N],ch[N][2],siz[N],rt,val[N],reimu;
  12. //////////////////////////////////////////////
  13. struct A {
  14. int num,id,v;
  15. }a[N];
  16. bool cmp1(const A &x,const A &y){return x.num<y.num;}
  17. bool cmp2(const A &x,const A &y){return x.id<y.id;}
  18. ///////////////////////////////////////////////
  19. void build_suffix_array() {
  20. int i,j,p,*x=wa,*y=wb,*t,m=n;
  21. for(i=0;i<m;i++) ws[i]=0;
  22. for(i=0;i<n;i++) ws[x[i]=r[i]]++;
  23. for(i=1;i<m;i++) ws[i]+=ws[i-1];
  24. for(i=n-1;i>=0;i--) sa[--ws[x[i]]]=i;
  25. for(j=p=1;p<n;j<<=1,m=p) {
  26. for(p=0,i=n-j;i<n;i++) y[p++]=i;
  27. for(i=0;i<n;i++) if(sa[i]-j>=0) y[p++]=sa[i]-j;
  28. for(i=0;i<n;i++) wv[i]=x[y[i]];
  29. for(i=0;i<m;i++) ws[i]=0;
  30. for(i=0;i<n;i++) ws[wv[i]]++;
  31. for(i=1;i<m;i++) ws[i]+=ws[i-1];
  32. for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
  33. for(t=x,x=y,y=t,i=p=1,x[sa[0]]=0;i<n;i++) {
  34. if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j]) x[sa[i]]=p-1;
  35. else x[sa[i]]=p++;
  36. }
  37. }
  38. for(i=1;i<n;i++) rank[sa[i]]=i;
  39. for(i=p=0;i<n-1;height[rank[i++]]=p)
  40. for(p?p--:0,j=sa[rank[i]-1];r[i+p]==r[j+p];p++);
  41. }
  42. /////////////////////////////////////////////
  43. int newnode(int x) {
  44. siz[++reimu]=1; val[reimu]=x; return reimu;
  45. }
  46. void pushup(int p) {
  47. siz[p]=siz[ls]+siz[rs]+1;
  48. }
  49. void rotate(int x) {
  50. int y=f[x],z=f[y],k=get(x);
  51. ch[y][k]=ch[x][!k]; f[ch[y][k]]=y;
  52. ch[x][!k]=y; f[y]=x; f[x]=z;
  53. if(z) ch[z][ch[z][1]==y]=x;
  54. pushup(y); pushup(x);
  55. if(rt==y) rt=x;
  56. }
  57. void splay(int x,int y) {
  58. for(int fa;(fa=f[x])!=y;rotate(x))
  59. if(f[fa]!=y)
  60. rotate(get(x)==get(fa)?fa:x);
  61. }
  62. void insert(int x) {
  63. int p=rt,l,r;
  64. while(p) {
  65. if(val[p]>=x) r=p,p=ls;
  66. else l=p,p=rs;
  67. }
  68. splay(l,0); splay(r,rt);
  69. ch[r][0]=newnode(x);
  70. f[reimu]=r; pushup(r); pushup(l);
  71. }
  72. int pre(int x) {
  73. int p=rt,ans;
  74. while(p) {
  75. if(val[p]>=x) p=ls;
  76. else ans=p,p=rs;
  77. }
  78. return val[ans];
  79. }
  80. int nxt(int x) {
  81. int p=rt,ans;
  82. while(p) {
  83. if(val[p]<=x) p=rs;
  84. else ans=p,p=ls;
  85. }
  86. return val[ans];
  87. }
  88. /////////////////////////////////////////
  89. struct ST {
  90. int f[N][20],L[N];
  91. void init() {
  92. int i,j;
  93. memset(f,0x3f,sizeof(f));
  94. L[1]=0;
  95. for(i=2;i<=n;i++) L[i]=L[i>>1]+1;
  96. for(i=0;i<=n;i++) {
  97. f[i][0]=height[i];
  98. }
  99. for(j=1;(1<<j)<=n;j++) {
  100. for(i=0;i+(1<<j)-1<=n;i++) {
  101. f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]);
  102. }
  103. }
  104. }
  105. int get_min(int l,int r) {
  106. int len=L[r-l+1];
  107. return min(f[l][len],f[r-(1<<len)+1][len]);
  108. }
  109. }S;
  110. int main() {
  111. scanf("%d",&n);
  112. int i;
  113. for(i=1;i<=n;i++) scanf("%d",&a[n-i+1].num),a[i].id=i;
  114. sort(a+1,a+n+1,cmp1);
  115. int j=0;a[0].num=23333443;
  116. for(i=1;i<=n;i++) {
  117. if(a[i].num!=a[i-1].num) j++;
  118. a[i].v=j;
  119. }
  120. sort(a+1,a+n+1,cmp2);
  121. for(i=0;i<n;i++) {
  122. r[i]=a[i+1].v;
  123. }
  124. r[n++]=0;
  125. build_suffix_array();
  126.  
  127. /*for(i=0;i<n;i++) printf("%d ",r[i]); puts("");
  128. for(i=0;i<n;i++) printf("%d ",sa[i]); puts("");
  129. for(i=0;i<n;i++) printf("%d ",height[i]); puts("");
  130. for(i=0;i<n;i++) printf("%d ",rank[i]);puts("");*/
  131.  
  132. ll ans=0;
  133. rt=newnode(-100000000);
  134. ch[rt][1]=newnode(100000000);
  135. f[ch[rt][1]]=rt;
  136. pushup(rt);
  137.  
  138. S.init();
  139. for(i=n-2;i>=0;i--) {
  140. ans+=n-i-1;
  141. int pr=pre(rank[i]);
  142. int tmp=0;
  143. if(pr>=0) {
  144. tmp=S.get_min(pr+1,rank[i]);
  145. }
  146. int nx=nxt(rank[i]);
  147. if(nx<=n) {
  148. tmp=max(tmp,S.get_min(rank[i]+1,nx));
  149. }
  150. ans-=tmp;
  151. insert(rank[i]);
  152. printf("%lld\n",ans);
  153. }
  154. }
  155. /*
  156. 7
  157. 1 2 3 3 3 1 2
  158. */

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

  1. bzoj千题计划283:bzoj4516: [Sdoi2016]生成魔咒(后缀数组)

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

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

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

  3. BZOJ4516 [Sdoi2016]生成魔咒 【后缀自动机】

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

  4. cogs2223. [SDOI2016 Round1] 生成魔咒(后缀数组 hash 二分 set

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

  5. 2018.12.23 bzoj4516: [Sdoi2016]生成魔咒(后缀自动机)

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

  6. [SDOI2016]生成魔咒(后缀自动机)

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

  7. 【洛谷 P4070】 [SDOI2016]生成魔咒(后缀自动机)

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

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

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

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

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

随机推荐

  1. Mac电脑配置Apache服务器详细说明

    Mac电脑服务器配置过程,无论是个人学习,还是公司测试都非常实用,流程精简易懂,用于让Mac电脑做服务器方便做网络数据请求的测试. 第一步:定位到 Apache2 目录 $ cd /etc/Apach ...

  2. jquery-取消冒泡

    1.通过返回false来取消默认的行为并阻止事件起泡. jQuery 代码: $("form").bind( "submit", function() { re ...

  3. PLSQL Developer使用技巧

    本文由liuyk80贡献 ·PL/SQL Developer 使用技巧 1.PL/SQL Developer 记住登陆密码 在使用 PL/SQL Developer 时,为了工作方便希望 PL/SQL ...

  4. oracle数据库中的trim不起作用

    在项目中使用datastage软件将sqlserver数据库的数据导入到oracle中的时候,出现了一些空格,然而使用trim相对应的字段发现没有作用,空格还存在,并没有去掉. 使用length(.. ...

  5. CSS position 笔记+实验

    目录: 1.static 2.relative 3.absolute 4.fixed 5.实验:static, relative, absolute中,父元素-子元素高度关系 6.z-index 7. ...

  6. Day7组合

    可以将那些重复的,固定的东西提出来,单独定义一个类. 例如: class Course: def __init__(self,course_name,course_period,course_pric ...

  7. go Mutex (互斥锁)和RWMutex(读写锁)

    转载自: https://blog.csdn.net/skh2015java/article/details/60334437 golang中sync包实现了两种锁Mutex (互斥锁)和RWMute ...

  8. ubuntu10.04 安装oracle server 版 笔记

    1:从oracle 官网下载oracle 10g ,然后解压出一个database文件夹. 2 :创建RedHat的版本声明文件[默认ubuntu无法通过oracle 的检查] 在/etc/redha ...

  9. C++神奇算法库——#include<algorithm>

    算法(Algorithm)为一个计算的具体步骤,常用于计算.数据处理和自动推理.C++ 算法库(Algorithms library)为 C++ 程序提供了大量可以用来对容器及其它序列进行算法操作的函 ...

  10. 用ASP.NET Core 2.0 建立规范的 REST API

    什么是REST REST 是 Representational State Transfer 的缩写. 它是一种架构的风格, 这种风格基于一套预定义的规则, 这些规则描述了网络资源是如何定义和寻址的. ...