1. 程式人生 > >[Luogu P4070] [BZOJ 4516] [SDOI2016]生成魔咒

[Luogu P4070] [BZOJ 4516] [SDOI2016]生成魔咒

洛谷傳送門

題目描述

魔咒串由許多魔咒字元組成,魔咒字元可以用數字表示。例如可以將魔咒字元 1,21,2 拼湊起來形成一個魔咒串 [1,2][1,2]

一個魔咒串 SS 的非空字串被稱為魔咒串 SS 的生成魔咒。

例如 S=[1,2,1]S=[1,2,1] 時,它的生成魔咒有 [1][1][2][2][1,2][1,2][2,1][2,1][1,2,1][1,2,1] 五種。S=[1,1,1]S=[1,1,1] 時,它的生成魔咒有 [1][1][1,1][1,1]

[1,1,1][1,1,1] 三種。最初 SS 為空串。共進行 nn 次操作,每次操作是在 SS 的結尾加入一個魔咒字元。每次操作後都需要求出,當前的魔咒串 SS 共有多少種生成魔咒。

輸入輸出格式

輸入格式:

第一行一個整數 nn

第二行 nn 個數,第 ii 個數表示第 ii 次操作加入的魔咒字元。

輸出格式:

輸出 nn 行,每行一個數。第 ii 行的數表示第 ii 次操作後 SS 的生成魔咒數量

輸入輸出樣例

輸入樣例#1:

7
1 2 3 3 3 1 2

輸出樣例#1:

1
3
6
9
12
17
22

說明

對於10%的資料,1n101 \le n \le 10

對於30%的資料,1n1001 \le n \le 100

對於60%的資料,1n10001 \le n \le 1000

對於100%的資料,1n1000001 \le n \le 100000

用來表示魔咒字元的數字 xx 滿足1n1091 \le n \le 10^9

解題分析

動態維護本質不同的子串數, 實際上就是SAMSAM上每個點的lenlen減去其parentparent節點的lenlen的和。 我們在修改parnetparnet的時候順帶維護即可。

程式碼如下:

#include <cstdio>
#include
<cstring>
#include <cstdlib> #include <cctype> #include <algorithm> #include <cmath> #include <map> #define R register #define IN inline #define W while #define MX 505000 #define ll long long int par[MX], len[MX]; std::map <int, int> to[MX]; int last, cur, l, cnt; ll ans; namespace SAM { IN int get(R int now) {return (~par[now]) ? len[now] - len[par[now]] : 0;} IN void insert(R int id) { R int now = last, tar; cur = ++cnt; len[cur] = len[last] + 1; last = cur; for (; (~now) && !to[now][id]; now = par[now]) to[now][id] = cur; if(now < 0) return par[cur] = 0, ans += get(cur), void(); tar = to[now][id]; if(len[tar] == len[now] + 1) return par[cur] = tar, ans += get(cur), void(); int nw = ++cnt; len[nw] = len[now] + 1; par[nw] = par[tar]; ans += get(nw) - get(tar); par[tar] = par[cur] = nw; ans += get(cur) + get(tar); to[nw] = to[tar]; for (; (~now) && to[now][id] == tar; now = par[now]) to[now][id] = nw; } } int main(void) { par[0] = -1; int T, buf; scanf("%d", &T); W (T--) { scanf("%d", &buf); SAM::insert(buf); printf("%lld\n", ans); } }