bzoj 4516: [Sdoi2016]生成魔咒 (后缀自动机)

来源:互联网 发布:域名购买哪家网站靠谱 编辑:程序博客网 时间:2024/05/18 14:43

4516: [Sdoi2016]生成魔咒

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 652  Solved: 359
[Submit][Status][Discuss]

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

Source

鸣谢Menci上传

[Submit][Status][Discuss]


题解:后缀自动机

对于一个状态s,他的right集合代表的子串的长度就是(len[fa],len[s]]。这道题我们需要动态的维护不同子串的个数,每次从头扫一遍直接计算肯定不行,我们考虑加入一个新字符会产生多少新的不同子串,这个个数其实就是(len[fa],len[s]]的区间长度。

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#include<map>#define N 200003#define LL long long using namespace std;int n,m,cnt,last,root,np,nq,p,q;int fa[N],l[N],a[N];LL ans;map<int,int> ch[N];void extend(int x){int c=a[x];p=last; np=++cnt; last=np;l[np]=x;for (;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;if (!p) fa[np]=root;else {q=ch[p][c];if (l[q]==l[p]+1) fa[np]=q;else{nq=++cnt; l[nq]=l[p]+1;ch[nq]=ch[q];fa[nq]=fa[q];fa[q]=fa[np]=nq;for (;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;}}if (fa[np]==root) ans+=(LL)l[np];else ans+=(LL)(l[np]-l[fa[np]]);}int main(){freopen("a.in","r",stdin);scanf("%d",&n);for (int i=1;i<=n;i++) scanf("%d",&a[i]);last=root=++cnt;for (int i=1;i<=n;i++) { //ans+=i; extend(i); printf("%I64d\n",ans);    }}



0 0
原创粉丝点击