反串的sam的parent tree就是后缀树...而且每个节点的$v$就是后缀树每个节点代表的字符串长度
因为在反串的某个子串往前延伸一个字符对应着sam的parent tree中的儿子,也对应着原串中后缀树的分岔
所以用这种方式构建出后缀树,那么后缀的lcp就是lca了,直接在树上统计答案即可,对每个节点统计有多少个节点对的lca是这个节点即可
#include<stdio.h> #include<string.h> typedef long long ll; struct sam{ int ch[26],fa,v; }t[1000010]; char s[500010]; int c[1000010],sa[1000010],siz[1000010]; int M=1,las=1; void extend(int c){ int p=las,np=++M,q,nq; t[np].v=t[p].v+1; siz[np]=1; while(p&&t[p].ch[c]==0){ t[p].ch[c]=np; p=t[p].fa; } if(!p) t[np].fa=1; else{ q=t[p].ch[c]; if(t[q].v==t[p].v+1) t[np].fa=q; else{ nq=++M; t[nq]=t[q]; t[nq].v=t[p].v+1; t[np].fa=t[q].fa=nq; while(p&&t[p].ch[c]==q){ t[p].ch[c]=nq; p=t[p].fa; } } } las=np; } void sort(){ int i; for(i=1;i<=M;i++)c[t[i].v]++; for(i=1;i<=M;i++)c[i]+=c[i-1]; for(i=M;i>0;i--)sa[c[t[i].v]--]=i; } int main(){ int n,i,x; ll ans; scanf("%s",s); n=strlen(s); for(i=n-1;i>=0;i--)extend(s[i]-'a'); sort(); ans=(ll)n*(n-1)*(n+1)/2; for(i=M;i>1;i--){ x=sa[i]; ans-=(ll)siz[x]*siz[t[x].fa]*t[t[x].fa].v*2; siz[t[x].fa]+=siz[x]; } printf("%lld",ans); }