SDOI2016生成魔咒

Posted 蒟蒻ZJO :-)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SDOI2016生成魔咒相关的知识,希望对你有一定的参考价值。

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


1 2 3 3 3 1 2

Sample Output





12 
17 
22

Hint

数据约束: 
对于 10% 的数据,1≤n≤10。 
对于 30% 的数据,1≤n≤100。 
对于 60% 的数据,1≤n≤1000。 
对于 100% 的数据,1≤n≤100000。 
用来表示魔咒字符的数字 x 满足 1≤x≤109。

 

反过来后,每多一个原来的前缀,相当于多一个现在的后缀.
注意到一个事(tao)实(lu).

每次新加入的那个后缀与前面加的后缀的lcp一定是前面名次与他最相近的两个后缀与他的lcp的最大值,

因为名次越近相似度越高.

所以维护一个set和ST表,每次求出这个值,最终产生的答案就是这个后缀的长度-lcp.


 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<cstring>
 5 #include<string>
 6 #include<algorithm>
 7 #include<map>
 8 #include<complex>
 9 #include<queue>
10 #include<stack>
11 #include<cmath>
12 #include<set>
13 #include<vector>
14 #define LL long long
15 #define maxn 100010
16 using namespace std;
17 int s[maxn],sa[maxn],rk[maxn],tmp[maxn],n,k;
18 LL lcp[maxn],ans[maxn],f[maxn][21];
19 set<int>se;
20 bool cmp(int i,int j){
21   if(rk[i]!=rk[j]) return rk[i]<rk[j];
22   else {
23     int ri=i+k<=n?rk[i+k]:-1,rj=j+k<=n?rk[j+k]:-1;
24     return ri<rj;
25   }
26 }
27 void get_sa(){
28   for(int i=0;i<=n;i++)
29     sa[i]=i,rk[i]=i<n?s[i]:-1;
30   for(k=1;k<=n;k*=2){
31     sort(sa,sa+n+1,cmp);
32     tmp[sa[0]]=0;
33     for(int i=1;i<=n;i++)
34       tmp[sa[i]]=tmp[sa[i-1]]+(cmp(sa[i-1],sa[i])?1:0);
35     for(int i=0;i<=n;i++)
36       rk[i]=tmp[i];
37   }
38 }
39 void get_lcp(){
40   for(int i=0;i<=n;i++) rk[sa[i]]=i;
41   int h=0;lcp[0]=0;
42   for(int i=0;i<n;i++){
43     int j=sa[rk[i]-1];
44     if(h>0) h--;
45     for(;j+h<n && i+h<n;h++)
46       if(s[j+h]!=s[i+h]) break;
47     lcp[rk[i]-1]=h;
48   }
49 }
50 int main(){
51  // freopen("!.in","r",stdin);
52  // freopen("!.out","w",stdout);
53   scanf("%d",&n);
54   for(int i=n-1;i>=0;i--)
55     scanf("%d",&s[i]);
56   get_sa();get_lcp();
57   for(int i=0;i<=n;i++) f[i][0]=lcp[i];
58   for(int j=1;j<=18;j++)
59     for(int i=0;i+(1<<(j-1))<=n;i++)
60       f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
61   se.insert(rk[n-1]);
62   ans[n-1]=1;
63   for(int i=n-2;i>=0;i--){
64     int lcpa=0,lcpb=0;
65     se.insert(rk[i]);
66     set<int>::iterator it=se.find(rk[i]);
67     if(it!=se.begin()){
68       it--;
69       int len=log(rk[i]-*it)/log(2);
70       if(1<<(len+1)<=rk[i]-*it) len++;
71       lcpa=min(f[*it][len],f[rk[i]-(1<<len)][len]);
72       it++;
73     }
74     if((++it)!=se.end()){
75       int len=log(*it-rk[i])/log(2);
76       if(1<<(len+1)<=rk[i]-*it) len++;
77       lcpb=min(f[rk[i]][len],f[*it-(1<<len)][len]);
78     }
79     ans[i]=n-i-max(lcpa,lcpb);
80   }
81   for(int i=n-1;i>=0;i--)
82     ans[i]+=ans[i+1],printf("%lld\n",ans[i]);
83   return 0;
84 }

以上是关于SDOI2016生成魔咒的主要内容,如果未能解决你的问题,请参考以下文章

LibreOJ #2033. 「SDOI2016」生成魔咒

bzoj 4516: [Sdoi2016]生成魔咒

bzoj4516 [Sdoi2016]生成魔咒

BZOJ4516SDOI2016生成魔咒 [SAM]

Bzoj4516 [Sdoi2016]生成魔咒

4516. [SDOI2016]生成魔咒后缀自动机