bzoj 3238: [Ahoi2013]差异【SAM+树形dp】

首先只有lcp(i,j)需要考虑
因为SAM的parent树是后缀的前缀的最长公共后缀(……),所以把这个串倒过来建SAM,这样就变成了求两个前缀的最长公共后缀,长度就是这两个前缀在parent树上的lcs对应的最大长度dis
这里用treedp解决即可,就是合并一下size

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1000005;
int n,fa[N],ch[N][27],dis[N],si[N],cur=1,con=1,la,h[N],cnt;
long long ans;
char s[N];
struct qwe
{
    int ne,to;
}e[N<<1];
void add(int u,int v)
{//cerr<<u<<" "<<v<<endl;
    cnt++;
    e[cnt].ne=h[u];
    e[cnt].to=v;
    h[u]=cnt;
}
void ins(int c,int id)
{
    la=cur,dis[cur=++con]=id;
    int p=la;
    for(;p&&!ch[p][c];p=fa[p])
        ch[p][c]=cur;
    if(!p)
        fa[cur]=1;
    else
    {
        int q=ch[p][c];
        if(dis[q]==dis[p]+1)
            fa[cur]=q;
        else
        {
            int nq=++con;
            dis[nq]=dis[p]+1;
            memcpy(ch[nq],ch[q],sizeof(ch[q]));
            fa[nq]=fa[q];
            fa[q]=fa[cur]=nq;
            for(;ch[p][c]==q;p=fa[p])
                ch[p][c]=nq;
        }
    }
    si[cur]=1;
}
void dfs(int u)
{
    for(int i=h[u];i;i=e[i].ne)
    {
        dfs(e[i].to);
        ans-=2ll*dis[u]*si[u]*si[e[i].to];
        si[u]+=si[e[i].to];
    }
}
int main()
{
    scanf("%s",s+1);
    n=strlen(s+1);
    reverse(s+1,s+1+n);
    for(int i=1;i<=n;i++)
        ins(s[i]-'a',i);
    for(int i=2;i<=con;i++)
        add(fa[i],i);
    dfs(1);
    for(int i=1;i<=n;i++)
        ans+=1ll*i*(n-1);
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/lokiii/p/10009965.html