luogu P4248 [AHOI2013]差异
题面传送门
这种东西一看就是要后缀什么求的。
然后发现lcp其实就是SAM上的LCA。
然后就可以做了。
但是其实这道题的式子很奇怪,其实就是SAM上的任意两个后缀代表的点的后缀和。
所以可以用简单的方法统计所有路径和即统计一条边经过几次而不用拆式子然后树形dp。
时间复杂度\(O(n)\)
code:
#include<cstdio>
#include<cstring>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define beg(x) int cur=s.h[x]
#define end cur
#define go cur=tmp.z
#define l(x) x<<1
#define r(x) x<<1|1
#define N 1000039
#define ll long long
using namespace std;
int n,m,k,x,y,z,cnt=1,last=1,g[N],siz[N],b[N],p;
ll ans;
char s[N];
struct sam{int len,link,son[26];}f[N];
I void insert(int x){
int p=last,now=last=++cnt,pus,cur;f[now].len=f[p].len+1;siz[now]++;
while(p&&!f[p].son[x]) f[p].son[x]=now,p=f[p].link;
if(!p)return (void)(f[now].link=1);
if(f[cur=f[p].son[x]].len==f[p].len+1)f[now].link=cur;
else {
f[pus=++cnt]=f[cur];f[pus].len=f[p].len+1;f[cur].link=f[now].link=pus;
while(p&&f[p].son[x]==cur)f[p].son[x]=pus,p=f[p].link;
}
}
int main(){
freopen("1.in","r",stdin);
register int i;
scanf("%s",s+1);n=strlen(s+1);
for(i=n;i;i--) insert(s[i]-'a');
for(i=1;i<=cnt;i++) g[f[i].len]++;
for(i=1;i<=cnt;i++)g[i]+=g[i-1];
for(i=1;i<=cnt;i++) b[g[f[i].len]--]=i;
for(i=cnt;i;i--) p=b[i],siz[f[p].link]+=siz[p],ans+=(ll)(f[p].len-f[f[p].link].len)*(n-siz[p])*siz[p];
printf("%lld\n",ans);
}