AHOI2013 差异

\(SAM\)中比较简单的题.
题目链接
其实这道题有一种用时间换空间的\(SA\)做法.
就是把\(Height\)建出来之后从大到小做,用一个并查集记录\(size\)即可.
时间复杂度\(O(n*\log n)\),空间复杂度\(O(n)\)
\(SAM\)的话就枚举一个点,算一下有多少种方案可以使两个点的\(lca\)是它即可.
算出方案再乘上\(len\)就好了.
时间复杂度\(O(n)\),空间复杂度\(O(n*26)\)

代码如下

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define N (2000010)
#define M (N<<1)
#define inf (0x7f7f7f7f)
#define rg register int
#define Label puts("NAIVE")
#define spa print(' ')
#define ent print('\n')
#define rand() (((rand())<<(15))^(rand()))
typedef long double ld;
typedef long long LL;
typedef unsigned long long ull;
using namespace std;
char s[N]; int n; LL ans;
struct SAM{
	int fa[N],ch[N][26],len[N],siz[N];
	int lst,cnt,fi[N],ne[M],b[M],E;
	SAM(){lst=cnt=1;}
	void add(int x,int y){
		ne[++E]=fi[x],fi[x]=E,b[E]=y;
	}
	void insert(int w,int L){
		int u=lst;len[lst=++cnt]=L,siz[cnt]=1;
		for(;u&&!ch[u][w];u=fa[u])ch[u][w]=cnt;
		if(!u){fa[cnt]=1;return;}
		int s=ch[u][w];
		if(len[u]+1==len[s]){fa[cnt]=s;return;}
		len[++cnt]=len[u]+1;
		for(int i=0;i<26;i++)ch[cnt][i]=ch[s][i];
		fa[cnt]=fa[s],fa[s]=fa[lst]=cnt;
		for(int i=u;ch[i][w]==s;i=fa[i])ch[i][w]=cnt;
	}
	void build(){for(int i=2;i<=cnt;i++)add(fa[i],i);}
	void solve(int u){
		for(int i=fi[u];i;i=ne[i]){
			int v=b[i]; solve(v);
			ans+=1ll*siz[v]*siz[u]*len[u],siz[u]+=siz[v];
		}
	}
}S;
int main(){
	scanf("%s",s+1),n=strlen(s+1);
	for(int i=1;i<=n;i++)S.insert(s[i]-'a',i);
	S.build(),S.solve(1);
	printf("%lld\n",1ll*(n-1)*n*(n+1)/2-2*ans);
}
posted @ 2018-12-21 18:55  Romeolong  阅读(160)  评论(0编辑  收藏  举报