BZOJ 4516. [Sdoi2016]生成魔咒【SAM 动态维护不同子串数量】

时间:2023-03-08 20:40:15
BZOJ 4516. [Sdoi2016]生成魔咒【SAM 动态维护不同子串数量】

[Sdoi2016]生成魔咒

动态维护不同子串的数量

想想如果只要查询一次要怎么做,那就是计算各个点的\(len[u]-len[link[u]]\)然后求和即可,现在要求动态更新,我们可以保存一个答案,然后每次更新后缀链接的时候,如果是连接的话就要加上\(len[u]-len[link[u]]\),断开的话就要减去\(len[u]-len[link[u]]\),每次输出答案即可

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<string>
#include<algorithm>
#include<stack>
using namespace std;
void ____(){ ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0); }
const int MAXN = 2e5+7;
using LL = int_fast64_t; struct SAM{
int len[MAXN],link[MAXN],tot,last;
LL ret = 0;
map<int,int> ch[MAXN];
SAM(){ link[0] = -1; }
void extend(int x){
int np = ++tot, p = last;
len[np] = len[p] + 1;
while(p!=-1 and !ch[p].count(x)){
ch[p].insert(make_pair(x,np));
p = link[p];
}
if(p==-1) link[np] = 0;
else{
int q = ch[p][x];
if(len[p]+1==len[q]) link[np] = q;
else{
int clone = ++tot;
len[clone] = len[p] + 1;
link[clone] = link[q];
ret += len[clone] - len[link[clone]];
ch[clone] = ch[q];
ret -= len[q] - len[link[q]];
link[q] = link[np] = clone;
ret += len[q] - len[clone];
while(p!=-1 and ch[p].count(x) and ch[p].at(x)==q){
ch[p].at(x) = clone;
p = link[p];
}
}
}
last = np;
ret += len[np] - len[link[np]];
}
}sam;
int n;
int main(){
scanf("%d",&n);
for(int i = 1; i <= n; i++){
int x; scanf("%d",&x);
sam.extend(x);
printf("%lld\n",sam.ret);
}
return 0;
}