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 共有多少种生成魔咒。

Solution

后缀自动机做又短又快

​后缀自动机学习小记​​​
每一次ans+=t[np].len−t[t[np].fa].len就可以了。

直接减trie空间不行

c++有一个map,简单的代替trie的数组。
不然,可以hash然后建邻接表。

Code

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cmath>
#include<map>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100007;
int i,j,k,l,n,m,last,num,yi,er;
long long ans;
struct node{
int len,fa;
}t[maxn*2];
map<int,int>son[maxn*2];
int np,nq,p,q;
void extend(int c){
p=last,np=++num;
t[np].len=t[p].len+1;
while(p&&!son[p][c])son[p][c]=np,p=t[p].fa;
if(!p)t[np].fa=1;
else{
q=son[p][c];
if(t[q].len==t[p].len+1)t[np].fa=q;
else{
nq=++num;
for(map<int,int>::iterator i=son[q].begin();i!=son[q].end();i++){
son[nq][i->first]=i->second;
}
t[nq]=t[q];
t[nq].len=t[p].len+1;
t[q].fa=t[np].fa=nq;
while(p&&son[p][c]==q)son[p][c]=nq,p=t[p].fa;
}
}
last=np;
}
int main(){
freopen("incantation.in","r",stdin);
freopen("incantation.out","w",stdout);
scanf("%d",&n);
last=num=1;
fo(i,1,n){
scanf("%d",&k);
extend(k);
ans+=t[np].len-t[t[np].fa].len;
printf("%lld\n",ans);
}
}