1. 程式人生 > >【BZOJ4516】生成魔咒(字尾自動機)

【BZOJ4516】生成魔咒(字尾自動機)

題面

BZOJ

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

題解

直接字尾自動機線上構造就行了
每次插入之後產生的新的貢獻就是
last.lenlast.parent.len
直接輸出即可

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm> #include<set> #include<map> #include<vector> #include<queue> using namespace std; #define ll long long #define RG register #define MAX 120000 inline int read() { RG int x=0,t=1;RG char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if
(ch=='-')t=-1,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*t; } int n; struct Node { map<int,int> son; int ff,len; }t[MAX<<1]; int last=1,tot=1; ll ans; void extend(int c) { int p=last,np=++tot;last=np; t[np].len=t[p].len+1; while(p&&!t[p].son[c])t[p].son[c]=np,p=t[p].ff; if(!p)t[np].ff=1; else { int q=t[p].son[c]; if(t[q].len==t[p].len+1)t[np].ff=q; else { int nq=++tot; t[nq]=t[q]; t[nq].len=t[p].len+1; t[q].ff=t[np].ff=nq; while(p&&t[p].son[c]==q)t[p].son[c]=nq,p=t[p].ff; } } ans+=t[np].len-t[t[np].ff].len; } int main() { n=read(); for(int i=1;i<=n;++i) { extend(read()); printf("%lld\n",ans); } return 0; }