bzoj3238:[Ahoi2013]差异

传送门

众所周知,两个后缀的最长公共前缀就是两个后缀在\(\rm sam\)上的\(\rm lca\)
那么对于这个题,我们发现那个式子其实就是\(\rm sam\)上的两点间的距离
我们就可以直接对于每条边算出贡献\((len_x-len_{fa_x})*size_x*(n-size_x)\)
然后就是答案了
代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
void read(int &x) {
    char ch; bool ok;
    for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
    for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define rg register
const int maxn=1e6+10;
int n,m,size[maxn],tot,las,pre[maxn],cnt,nxt[maxn],h[maxn];char a[maxn];
long long ans;
struct sam{int link,len,ch[26];}s[maxn];
void sam_pre(){s[1].len=0,s[1].link=-1;tot=las=1;}
void ins(int x){
    int cur=++tot,p=las;s[cur].len=s[las].len+1;size[tot]=1;
    while(p!=-1&&!s[p].ch[x])s[p].ch[x]=cur,p=s[p].link;
    if(p==-1)s[cur].link=1;
    else {
        int q=s[p].ch[x];
        if(s[q].len==s[p].len+1)s[cur].link=q;
        else {
            int now=++tot;s[now].len=s[p].len+1;
            s[now].link=s[q].link;
            memcpy(s[now].ch,s[q].ch,sizeof s[q].ch);
            while(p!=-1&&s[p].ch[x]==q)s[p].ch[x]=now,p=s[p].link;
            s[cur].link=s[q].link=now;
        }
    }
    las=cur;
}
void add(int x,int y){pre[++cnt]=y,nxt[cnt]=h[x],h[x]=cnt;}
void dfs(int x){
    for(rg int i=h[x];i;i=nxt[i]){
        dfs(pre[i]),size[x]+=size[pre[i]];
        ans+=1ll*(s[pre[i]].len-s[x].len)*size[pre[i]]*(n-size[pre[i]]);
    }
}
int main(){
    scanf("%s",a+1),n=strlen(a+1),sam_pre();
    for(rg int i=1;i<=n;i++)ins(a[i]-'a');
    for(rg int i=2;i<=tot;i++)add(s[i].link,i);
    dfs(1),printf("%lld\n",ans);
}
posted @ 2019-07-01 19:56 蒟蒻--lichenxi 阅读(...) 评论(...) 编辑 收藏