[bzoj3238] [Ahoi2013] 差异

Description

给定一个长度为 (n) 的字符串 (S),令 (Ti) 表示它从第 (i) 个字符开始的后缀。求
(sumlimits_{1 leq i <j leq n} len(Ti)+len(Tj)-2 imes lcp(Ti,Tj))
其中,(len(a)) 表示字符串 (a) 的长度,(lcp(a,b)) 表示字符串 (a) 和字符串 (b) 的最长公共前缀。

Input

一行,一个字符串 (S)

Output

一行,一个整数,表示所求值。

Sample Input

cacao

Sample Output

54

HINT

对于 (100%) 的数据,保证 (2 leq n leq 500000),且均为小写字母。


想法

化简一下要求的那个式子

[egin{equation*} egin{aligned} &sumlimits_{1 leq i <j leq n} len(Ti)+len(Tj)-2 imes lcp(Ti,Tj) \ =&frac{n(n+1)(n-1)}{2} -2 imes sumlimits_{1 leq i <j leq n} lcp(Ti,Tj) end{aligned} end{equation*} ]

不妨设 (x= sumlimits_{1 leq i <j leq n} lcp(Ti,Tj)) ,我们要求的就是它
建出后缀自动机,对于其中每个节点,预处理出它可以到的结束节点的个数 (size) ,这个点对 (x) 的贡献就是 (frac{size(size-1)}{2})


代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 500005;
typedef long long ll;

struct node{
    int len,size;
    node *ch[26],*pa;
}pool[N*2],*root,*last;
int cnt;
void insert(int c){
    node *p=last,*cur=&pool[++cnt];
    cur->len=p->len+1;
    for(;p && !p->ch[c];p=p->pa) p->ch[c]=cur;
    if(!p) cur->pa=root;
    else{
        node *q=p->ch[c],*nq;
        if(q->len==p->len+1) cur->pa=q;
        else{
            nq=&pool[++cnt];
            nq->len=p->len+1;
            for(int i=0;i<26;i++) nq->ch[i]=q->ch[i];
            nq->pa=q->pa;
            q->pa=cur->pa=nq;
            for(;p && p->ch[c]==q;p=p->pa) p->ch[c]=nq;
        }
    }
    last=cur;
}

char s[N];
int n;

ll ans,del[N*2];
int vis[N*2];
void Get_size(node *p){
    if(vis[p-pool]) {
        ans-=del[p-pool];
        return;
    }
    ll pre=ans;
    for(int i=0;i<26;i++){
        if(!p->ch[i]) continue;
        Get_size(p->ch[i]);
        p->size+=p->ch[i]->size;
    }
    if(p->size>=2) ans-=(ll)p->size*(p->size-1);
    del[p-pool]=pre-ans; vis[p-pool]=1;
}

int main()
{
    scanf("%s",s);
    n=strlen(s);
    
    root=&pool[++cnt];
    last=root;
    for(int i=0;i<n;i++) insert(s[i]-'a');
    
    node *tmp=last;
    for(;tmp!=root;tmp=tmp->pa) tmp->size=1;
    ans=(ll)n*(n+1)/2*(n-1);
    Get_size(root);
    
    printf("%lld
",ans+(ll)n*(n-1));
    
    return 0;
}
既然选择了远方,便只顾风雨兼程
原文地址:https://www.cnblogs.com/lindalee/p/10702640.html