P4248 [AHOI2013]差异 SAM

题意:

戳这里

分析:

我们观察题面发现这个式子长得很像树上两点间的距离,这就让我们联想到建一颗 \(SAM\),然后构建出 \(parent\) 树,原式等价于求 \(parent\) 树上任意两点间距离的和值

典型的树上 \(dp\),我们考虑每一条边的贡献,每一条边对原式的贡献等价于 \(经过该边的点对的数目\times 边的长度\)

所以我们可以推出 \(ans=\sum (len[i]-len[link[i]])*(n-siz[i])*siz[i]\)

代码:

#include<bits/stdc++.h>

using namespace std;

namespace zzc
{
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
        return x*f;
    }

    const int maxn = 1e6+5;
    int n;
    long long ans=0;
    char ch[maxn];

    struct suffix_automaton
    {
        int cnt,lst;
        int link[maxn],len[maxn],trans[maxn][26],t[maxn],sa[maxn],siz[maxn];
        suffix_automaton(){cnt=lst=1;}
        
        void insert(int x)
        {
            int cur=++cnt,tmp=lst;lst=cnt;
            len[cur]=len[tmp]+1;siz[cur]=1;
            for(;tmp&&!trans[tmp][x];tmp=link[tmp]) trans[tmp][x]=cur;
            if(!tmp)
            {
                link[cur]=1;
            }
            else
            {
                int q=trans[tmp][x];
                if(len[tmp]+1==len[q])
                {
                    link[cur]=q;
                }
                else
                {
                    int clone=++cnt;
                    len[clone]=len[tmp]+1;
                    link[clone]=link[q];
                    link[q]=link[cur]=clone;
                    for(int i=0;i<26;i++) trans[clone][i]=trans[q][i];
                    for(;tmp&&trans[tmp][x]==q;tmp=link[tmp]) trans[tmp][x]=clone;
                }
            }
        }

        void sort()
        {
            for(int i=1;i<=cnt;i++) t[len[i]]++;
            for(int i=1;i<=cnt;i++) t[i]+=t[i-1];
            for(int i=1;i<=cnt;i++) sa[t[len[i]]--]=i;
            for(int i=cnt;i>=1;i--)
            {
                int x=sa[i];siz[link[x]]+=siz[x];
                ans+=1ll*(n-siz[x])*siz[x]*(len[x]-len[link[x]]);
            }
        }

    }sam;

    void work()
    {
        scanf("%s",ch+1);n=strlen(ch+1);
        for(int i=n;i>=1;i--) sam.insert(ch[i]-'a');
        sam.sort();
        printf("%lld\n",ans);
    }


}

int main()
{
    zzc::work();
    return 0;
}
posted @ 2020-12-28 23:59  youth518  阅读(66)  评论(0编辑  收藏  举报