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
7
1 2 3 3 3 1 2
Sample Output
1
3
6
9
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生成魔咒的主要内容,如果未能解决你的问题,请参考以下文章