[Luogu P4070] [BZOJ 4516] [SDOI2016]生成魔咒
阿新 • • 發佈:2018-12-10
洛谷傳送門
題目描述
魔咒串由許多魔咒字元組成,魔咒字元可以用數字表示。例如可以將魔咒字元 拼湊起來形成一個魔咒串 。
一個魔咒串 的非空字串被稱為魔咒串 的生成魔咒。
例如 時,它的生成魔咒有 、、、、 五種。 時,它的生成魔咒有 、、 三種。最初 為空串。共進行 次操作,每次操作是在 的結尾加入一個魔咒字元。每次操作後都需要求出,當前的魔咒串 共有多少種生成魔咒。
輸入輸出格式
輸入格式:
第一行一個整數 。
第二行 個數,第 個數表示第 次操作加入的魔咒字元。
輸出格式:
輸出 行,每行一個數。第 行的數表示第 次操作後 的生成魔咒數量
輸入輸出樣例
輸入樣例#1:
7
1 2 3 3 3 1 2
輸出樣例#1:
1
3
6
9
12
17
22
說明
對於10%的資料,
對於30%的資料,
對於60%的資料,
對於100%的資料,
用來表示魔咒字元的數字 滿足
解題分析
動態維護本質不同的子串數, 實際上就是上每個點的減去其節點的的和。 我們在修改的時候順帶維護即可。
程式碼如下:
#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);
}
}