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。
题解:
SAM的裸题,但是打得后缀数组,显然是可以的,要求的是每个前缀的子串数,显然反转一下就变成了后缀的子串数
imagine一下求平时子串个数的过程,L-lcp+1 lcp为i和i+1的high[i] 显然这个题我们就可以类似的处理
加一个字符反转后就相当于加一个后缀,我们考虑他的贡献,显然之和他rk的前驱后继有关,于是我们减去原来的再加上现在的即可成为答案,前驱后继SET维护即可,或者玩玩SPLAY Treap也挺好
1 #include <algorithm> 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <set> 8 using namespace std; 9 const int N=100005,INF=2e8; 10 int n,sa[N],rk[N],high[N],c[N],x[N],b[N],bc[N],num=0,s[N],y[N],bel[N]; 11 int gi(){ 12 int str=0;char ch=getchar(); 13 while(ch>'9' || ch<'0')ch=getchar(); 14 while(ch>='0' && ch<='9')str=(str<<1)+(str<<3)+ch-48,ch=getchar(); 15 return str; 16 } 17 int midit(int x){ 18 int l=1,r=num,mid; 19 while(l<=r){ 20 mid=(l+r)>>1; 21 if(bc[mid]==x)return mid; 22 if(x>bc[mid])l=mid+1; 23 else r=mid-1; 24 } 25 } 26 int k; 27 bool comp(int i,int j){ 28 return y[i]==y[j] && y[i+k]==y[j+k]; 29 } 30 void Getsa(){ 31 int m=num,t; 32 for(int i=0;i<=m;i++)c[i]=0; 33 for(int i=1;i<=n;i++)c[x[i]=bel[i]]++; 34 for(int i=1;i<=m;i++)c[i]+=c[i-1]; 35 for(int i=n;i>=1;i--)sa[c[x[i]]--]=i; 36 for(k=1;k<=n;k<<=1){ 37 t=0; 38 for(int i=0;i<=m;i++)y[i]=0; 39 for(int i=n-k+1;i<=n;i++)y[++t]=i; 40 for(int i=1;i<=n;i++)if(sa[i]>k)y[++t]=sa[i]-k; 41 for(int i=0;i<=m;i++)c[i]=0; 42 for(int i=1;i<=n;i++)c[x[i]]++; 43 for(int i=1;i<=m;i++)c[i]+=c[i-1]; 44 for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i]; 45 swap(x,y); 46 x[sa[1]]=t=1; 47 for(int i=2;i<=n;i++)x[sa[i]]=comp(sa[i-1],sa[i])?t:++t; 48 if(t==n)break; 49 m=t; 50 } 51 for(int i=1;i<=n;i++)rk[sa[i]]=i; 52 } 53 void Gethight(){ 54 int j,h=0; 55 for(int i=1;i<=n;i++){ 56 j=sa[rk[i]-1]; 57 if(h)h--; 58 for(;j+h<=n && i+h<=n;h++)if(s[i+h]!=s[j+h])break; 59 high[rk[i]-1]=h; 60 } 61 } 62 long long ans[N]; 63 set<int>p; 64 int f[N][21]; 65 void prework(){ 66 int to; 67 for(int i=1;i<=n;i++)f[i][0]=high[i]; 68 for(int j=1;j<=20;j++) 69 for(int i=1,tmp=n-(1<<j)+1;i<=tmp;i++){ 70 to=i+(1<<(j-1)); 71 if(f[i][j-1]<f[to][j-1])f[i][j]=f[i][j-1]; 72 else f[i][j]=f[to][j-1]; 73 } 74 } 75 int query(int l,int r){ 76 int k=log(r-l+1)/log(2); 77 return min(f[l][k],f[r-(1<<k)+1][k]); 78 } 79 int lcp(int i,int j){ 80 return query(i,j-1); 81 } 82 long long cnt[N]; 83 void Getanswer(){ 84 long long sum=0; 85 int pre,nxt,now; 86 set<int>::iterator iter; 87 p.insert(n+1);p.insert(0); 88 for(int i=n;i>=1;i--){ 89 now=rk[i]; 90 iter=(--p.lower_bound(now)); 91 if(iter!=p.begin()){ 92 pre=*(iter); 93 sum-=cnt[pre]; 94 cnt[pre]=(n-sa[pre]+1-lcp(pre,now)); 95 sum+=cnt[pre]; 96 } 97 nxt=*(p.upper_bound(now)); 98 cnt[now]=(n-sa[now]+1-lcp(now,nxt)); 99 sum+=cnt[now]; 100 ans[n-i+1]=sum; 101 p.insert(now); 102 } 103 for(int i=1;i<=n;i++)printf("%lld\n",ans[i]); 104 } 105 int main() 106 { 107 freopen("menci_incantation.in","r",stdin); 108 freopen("menci_incantation.out","w",stdout); 109 n=gi(); 110 for(int i=1;i<=n;i++)s[i]=gi(); 111 for(int i=1,t=n>>1;i<=t;i++)swap(s[i],s[n-i+1]); 112 for(int i=1;i<=n;i++)b[i]=s[i]; 113 sort(b+1,b+n+1);for(int i=1;i<=n;i++)if(b[i]!=b[i+1])bc[++num]=b[i]; 114 for(int i=1;i<=n;i++)bel[i]=midit(s[i]); 115 Getsa();Gethight();prework();Getanswer(); 116 return 0; 117 }