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
1 2 3 3 3 1 2
Sample Output
1
3
6
9
12
17
22
3
6
9
12
17
22
yy万岁!
学SAM时让Rose给我举个例子SAM能干啥,他说求有多少各不同字串用right集合加加减减就可以了
但是这道题每加一个就要跑,真麻烦,不会,我们来yy一下吧!
SAM的性质:对于一个节点u,他能表示的不同字串就是Max(u)-Min(u)+1
其实就是Max(u)-(Min(u)-1)
其中Min(u)-1其实就是Max(parent(u))
为啥??
因为root到parent的每条路径都是到u的后缀,那就可以yy出来了
我们再来感性的yy一下,对于当前节点计算出来的结果是对于当前这个点来说的,比如
AB这个字符串,加入B之后ans就+2,A之前就已经计算过了,所以可以保证正确性(感觉我做SAM全靠YY?)
蒟蒻说话必有失误,如有不对请各路神犇指正
代码如下:
#include<map> #include<cmath> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> using namespace std; struct SAM{ int dep,parent; map<int ,int > son; }tr[210000];int last,root,cnt; typedef long long ll; ll ans=0ll; void add(int x,int pos) { int np=++cnt; tr[np].dep=pos; int p=last; while(p!=0&&tr[p].son[x]==0)tr[p].son[x]=np,p=tr[p].parent; if(p==0)tr[np].parent=root; else { int q=tr[p].son[x]; if(tr[q].dep==tr[p].dep+1)tr[np].parent=q; else { int nq=++cnt; tr[nq]=tr[q];tr[nq].dep=tr[p].dep+1; tr[q].parent=tr[np].parent=nq; while(p!=0&&tr[p].son[x]==q)tr[p].son[x]=nq,p=tr[p].parent; } } last=np; ans+=(ll)tr[np].dep-tr[tr[np].parent].dep; } int main() { int n; scanf("%d",&n); ans=0;root=last=++cnt; for(int i=1;i<=n;i++) { int x;scanf("%d",&x); add(x,i); printf("%lld\n",ans); } return 0; }
by_lmy