Problem
【SHOI2016】生成魔咒
T i m e L i m i t : 10 S e c \mathrm{Time\;Limit:\;10\;Sec} T i m e L i m i t : 1 0 S e c
M e m o r y L i m i t : 256 M B \mathrm{Memory\;Limit:\;256\;MB} M e m o r y L i m i t : 2 5 6 M B
Description
魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 1 1 1 ,2 2 2 拼凑起来形成一个魔咒串 [ 1 , 2 ] [1,2] [ 1 , 2 ] 。
一个魔咒串 S S S 的非空字串被称为魔咒串 S S S 的生成魔咒。例如 S = [ 1 , 2 , 1 ] S=[1,2,1] S = [ 1 , 2 , 1 ] 时,它的生成魔咒有 [ 1 ] [1] [ 1 ] ,[ 2 ] [2] [ 2 ] ,[ 1 , 2 ] [1,2] [ 1 , 2 ] ,[ 2 , 1 ] [2,1] [ 2 , 1 ] ,[ 1 , 2 , 1 ] [1,2,1] [ 1 , 2 , 1 ] 五种。S = [ 1 , 1 , 1 ] S=[1,1,1] S = [ 1 , 1 , 1 ] 时,它的生成魔咒有 [ 1 ] [1] [ 1 ] ,[ 1 , 1 ] [1,1] [ 1 , 1 ] ,[ 1 , 1 , 1 ] [1,1,1] [ 1 , 1 , 1 ] 三种。
最初 S S S 为空串。共进行 n n n 次操作,每次操作是在 S S S 的结尾加入一个魔咒字符。每次操作后都需要求出,当前的魔咒串 S S S 共有多少种生成魔咒。
第一行一个整数 n n n 。
第二行 n n n 个数,第 i i i 个数表示第 i i i 次操作加入的魔咒字符
1 ≤ n ≤ 100000 1\le n\le100000 1 ≤ n ≤ 1 0 0 0 0 0 ,用来表示魔咒字符的数字 x x x 满足 1 ≤ x ≤ 1 0 9 1\le x\le10^9 1 ≤ x ≤ 1 0 9
Output
输出 n n n 行,每行一个数。第 i i i 行的数表示第 i i i 次操作后 S S S 的生成魔咒数量
Sample Output
Source
鸣谢Menci
上传
标签:后缀自动机
Solution
后缀自动机的模板题。
按题意增量构建后缀自动机,考虑每次新增的一位能构成多少新串,贡献计入答案。
对于每次新增的结点n p np n p ,若其在p a r e n t parent p a r e n t 树上的父亲为p a r par p a r ,那么前面有若干条路走到p a r par p a r 从而得到的字符串与其走到n p np n p 得到的字符串相同。由于走到p a r par p a r 的字符串个数是l e n p a r len_{par} l e n p a r ,走到n p np n p 的字符串个数是l e n n p len_{np} l e n n p 。因此新增字符串个数是l e n n p − l e n p a r len_{np}-len_{par} l e n n p − l e n p a r 。维护即可。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <bits/stdc++.h> #define MAX_N 100000 #define mii map<int,int> using namespace std;typedef long long lnt;template <class T > inline void read (T &x) { x = 0 ; int c = getchar (), f = 1 ; for (; !isdigit (c); c = getchar ()) if (c == 45 ) f = -1 ; for (; isdigit (c); c = getchar ()) (x *= 10 ) += f*(c-'0' ); } int n, rt, sz, lst; lnt ans;struct node {mii ch; int len, par;} SAM[(MAX_N<<1 )+5 ];int newnode (int _len) {SAM[++sz].len = _len; return sz;}void init () {sz = 0 , rt = lst = newnode (0 );}void insert (int c) { int p = lst, np = newnode (SAM[p].len+1 ); lst = np; for (; p && !SAM[p].ch[c]; p = SAM[p].par) SAM[p].ch[c] = np; if (!p) {SAM[np].par = rt; return ;} int q = SAM[p].ch[c]; if (SAM[q].len == SAM[p].len+1 ) {SAM[np].par = q; return ;} int nq = newnode (SAM[p].len+1 ); SAM[nq].ch = SAM[q].ch; SAM[nq].par = SAM[q].par, SAM[q].par = SAM[np].par = nq; for (; p && SAM[p].ch[c] == q; p = SAM[p].par) SAM[p].ch[c] = nq; } void upd () {ans += SAM[lst].len-SAM[SAM[lst].par].len;}int main () { read (n), init (); for (int i = 0 , x; i < n; i++) read (x), insert (x), upd (), printf ("%lld\n" , ans); return 0 ; }