Luogu P4248 [AHOI2013]差异

题链
可以把\(\sum\)拆开,只需要计算\(\sum_{i<j}LCP(suf[i],suf[j])\)
这显然是后缀数组裸题,所以我们采用后缀自动机来解决
后缀的前缀很难看,不妨翻转字符串,求前缀的后缀,应用parent tree
发现任意两个节点\(i,j\)的最长后缀为Len(LCA(i,j)),对每个节点求出子树中的点对,用DP统计

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int N=1e6+5;
int n,tot,lst,g[N],q[N],d[N];
ll ans;
char s[N];
struct A{
	int len,fa,c[30];
}dian[N];

inline void SAM(int ch) {
	int np=++tot,p=lst; lst=np;
	dian[np].len=dian[p].len+1; g[np]=1;
	for(;p&&!dian[p].c[ch];p=dian[p].fa) dian[p].c[ch]=np;
	if(!p) dian[np].fa=1;
		else {
			int q=dian[p].c[ch];
			if(dian[q].len==dian[p].len+1) dian[np].fa=q;
				else {
					int nq=++tot; dian[nq]=dian[q];
					dian[nq].len=dian[p].len+1;
					dian[q].fa=dian[np].fa=nq;
					for(;p&&dian[p].c[ch]==q;p=dian[p].fa) dian[p].c[ch]=nq;
				}
		}
}

int main(){
	scanf("%s",s+1); n=strlen(s+1);
	for(int i=1;i<=n/2;i++) swap(s[i],s[n-i+1]); 
	ans=(ll)(n-1)*n*(n+1)>>1;
	tot=lst=1;
	for(int i=1;i<=n;i++) SAM(s[i]-'a');
	int l=1,r=0;
	for(int i=1;i<=tot;i++) {
		d[dian[i].fa]++;
	}
	for(int i=1;i<=tot;i++) {
		if(!d[i]) q[++r]=i;
	}
	while(l<=r) {
		int u=q[l];l++;
		d[dian[u].fa]--;
		if(!d[dian[u].fa]) q[++r]=dian[u].fa;
		g[dian[u].fa]+=g[u];
	}
	for(int i=2;i<=tot;i++) {
		ans-=(ll)g[i]*(g[i]-1)*(dian[i].len-dian[dian[i].fa].len);
	}
	printf("%lld\n",ans);
	return 0;
}
上一篇:分治算法求点集中最短距离


下一篇:SAAAAAAAAM学习笔记。