Luogu-4248 [AHOI2013]差異
阿新 • • 發佈:2018-11-29
\(\sum_{i<j}len(i)+len(j)\)比較簡單,稍微想想就出來了,問題在於怎麼求任意兩個字尾的\(lcp\)長度之和
因為求\(lcp\)實際上就是一個對\(h\)陣列求區間最小值的過程,這就可以考慮計算對於每一個\(h\),他對答案做出的貢獻,可以看出以\(h[x]\)作為最小值的區間\([l,r]\)中,任意一對\(i\in[l,x],j\in[x,r]\)的\(lcp\)都是他,總的對數就是貢獻。\(l,r\)可用單調佇列來快速求出。
需要注意區間\([l,x],[x,r]\)中,最好一個是滿足\(h[i]<h[x]\),一個滿足\(h[i]<=h[x]\)
#include<map> #include<cmath> #include<queue> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int maxn=1e6+100; struct SA{ int sa[maxn],tp[maxn],rk[maxn],tax[maxn],h[maxn],n,m,st[maxn],top,l[maxn],r[maxn]; char s[maxn]; void Qsort(){ for(int i=0;i<=m;i++) tax[i]=0; for(int i=1;i<=n;i++) tax[rk[i]]++; for(int i=1;i<=m;i++) tax[i]+=tax[i-1]; for(int i=n;i>=1;i--) sa[tax[rk[tp[i]]]--]=tp[i]; } void getsa(){ m=200; for(int i=1;i<=n;i++) rk[i]=s[i],tp[i]=i; Qsort(); for(int p=1,w=1;p<n;m=p,w<<=1){ p=0; for(int i=1;i<=w;i++) tp[++p]=n+i-w; for(int i=1;i<=n;i++) if(sa[i]>w) tp[++p]=sa[i]-w; Qsort(); swap(tp,rk); rk[sa[1]]=p=1; for(int i=2;i<=n;i++) rk[sa[i]]=tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+w]==tp[sa[i-1]+w]?p:++p; } } void geth(){ for(int i=1,j,p=0;i<=n;h[rk[i++]]=p) for(p?p--:p,j=sa[rk[i]-1];s[i+p]==s[j+p];p++); } ll work(){ ll ans=0; for(int i=1;i<=n;i++) ans+=1ll*i*(n-1); h[0]=-0x7fffffff,top=0; for(int i=1;i<=n;i++){ while(h[st[top]]>=h[i]) top--; l[i]=st[top]+1; st[++top]=i; } h[n+1]=-0x7fffffff,top=0,st[top]=n+1; for(int i=n;i>=1;i--){ while(h[st[top]]>h[i]) top--; r[i]=st[top]-1; st[++top]=i; } for(int i=1;i<=n;i++) ans-=2ll*(i-l[i]+1)*(r[i]-i+1)*h[i]; return ans; } }sa; int main(){ // freopen(".in","r",stdin); scanf("%s",sa.s+1),sa.n=strlen(sa.s+1); sa.getsa(),sa.geth(); printf("%lld\n",sa.work()); return 0; }