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;
}
posted @ 2020-04-15 16:17  _kiko  阅读(95)  评论(0编辑  收藏  举报