bzoj 4516

考察后缀自动机的性质:每个节点上维护的子串数量等于自己与父节点的$len$之差

据此,我们在建后缀自动机的时候,对于每个插入的节点加上他与父节点的$len$之差即可

什么?分裂出来的节点怎么办?无所谓!不累计!

因为假设我们有一个点$p$,其父亲为$f$,分裂出的节点为$q$,那么我们知道,进行这次分裂之前,点$p$的贡献为$len_{p}-len_{f}$

而进行这次分裂之后,点$p$的贡献为$len_{p}-len_{q}$,点$q$的贡献为$len_{q}-len_{f}$,因此分裂后的总贡献仍为$len_{p}-len_{f}$,这个值不变,因此分裂操作不影响答案

建后缀自动机的时候用map存出边即可

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#define ll long long
using namespace std;
struct SAM
{
    map <int,int> M;
    int len,pre;
}s[200005];
int n;
ll ans=0;
int tot=1,las=1;
void ins(int c)
{
    int nwp=++tot;
    s[nwp].len=s[las].len+1;
    int lsp;
    for(lsp=las;lsp&&s[lsp].M.find(c)==s[lsp].M.end();lsp=s[lsp].pre)s[lsp].M[c]=nwp;
    if(!lsp)s[nwp].pre=1;
    else 
    {
        int lsq=s[lsp].M[c];
        if(s[lsq].len==s[lsp].len+1)s[nwp].pre=lsq;
        else
        {
            int nwq=++tot;
            s[nwq]=s[lsq];
            s[nwq].len=s[lsp].len+1;
            s[lsq].pre=s[nwp].pre=nwq;
            while(s[lsp].M[c]==lsq)s[lsp].M[c]=nwq,lsp=s[lsp].pre;
        }
    }
    ans+=(ll)(s[nwp].len-s[s[nwp].pre].len),las=nwp;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        ins(x);
        printf("%lld\n",ans);
    }
    return 0;
}

 

posted @ 2019-07-04 09:51  lleozhang  Views(56)  Comments(0Edit  收藏
levels of contents