您的位置:首页 > 其它

bzoj 3238: [Ahoi2013]差异 (后缀自动机+树形dp)

2016-12-22 07:22 489 查看

3238: [Ahoi2013]差异

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 2268  Solved: 1031

[Submit][Status][Discuss]

Description



Input

一行,一个字符串S

Output

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

Sample Input

cacao

Sample Output

54

HINT

2<=N<=500000,S由小写英文字母组成

Source



[Submit][Status][Discuss]


题解:后缀自动机

这道题需要知道后缀自动机的一个性质,两个串的最长公共后缀,位于这两个串对应状态在parent树的最近公共祖先上。

分析一下题目中的式子,发现sigma len(Ti)+len(Tj)可以直接进行求解。关键是如何求解lcp ,因为上面的性质求得是最长公共后缀,所以我们将原串倒置,建立后缀自动机,得到的最长公共后缀就是原串的最长公共前缀。

知道这个后,问题就变成了,如何求解某个状态是多少对节点的后缀,这个可以用树形dp的思想,从下往上依次更新。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 1000003
#define LL long long
using namespace std;
int n,m,cnt,root,last,np,nq,p,q;
int ch
[30],fa
,l
,a
,v
,r
,pos
;
LL sum
;
char s
;
void extend(int x)
{
int c=a[x];
p=last; np=++cnt; last=np;
l[np]=x;
for (;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
if (!p) fa[np]=root;
else {
q=ch[p][c];
if (l[q]==l[p]+1) fa[np]=q;
else {
nq=++cnt; l[nq]=l[p]+1;
memcpy(ch[nq],ch[q],sizeof ch[nq]);
fa[nq]=fa[q];
fa[q]=fa[np]=nq;
for (;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
}
int main()
{
freopen("a.in","r",stdin);
freopen("my.out","w",stdout);
scanf("%s",s+1);
n=strlen(s+1);
last=root=++cnt;
for (int i=1;i<=n;i++) a[n-i+1]=s[i]-'a';
for (int i=1;i<=n;i++) extend(i);
for (int i=1;i<=cnt;i++) v[l[i]]++;
for (int i=1;i<=n;i++) v[i]+=v[i-1];
for (int i=1;i<=cnt;i++) pos[v[l[i]]--]=i;
p=1;
for (int i=1;i<=n;i++) {
p=ch[p][a[i]]; r[p]++; sum[p]++;
}
for (int i=cnt;i;i--){
int t=pos[i];
r[fa[t]]+=r[t];
}
/*for (int i=1;i<=cnt;i++) cout<<r[i]<<" ";
cout<<endl;
for (int i=1;i<=cnt;i++) cout<<l[i]<<" ";
cout<<endl;
for (int i=1;i<=cnt;i++) cout<<pos[i]<<" ";
cout<<endl;*/
LL ans=0;
for (int i=cnt;i>=1;i--){
int t=pos[i];
ans+=(LL)l[fa[t]]*(LL)r[t]*(LL)sum[fa[t]];
sum[fa[t]]+=(LL)r[t];
}
// cout<<ans<<endl;
printf("%I64d\n",(LL)(n+1)*(LL)n/2*(LL)(n-1)-2*ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: