1. 程式人生 > >【bzoj3238】差異[AHOI2013](後綴數組+單調棧)

【bzoj3238】差異[AHOI2013](後綴數組+單調棧)

algorithm char tar 最小 one eight can ont 會有

  題目傳送門:http://www.lydsy.com/JudgeOnline/problem.php?id=3238

  這道題從大概半年以前就開始啃了,不過當年因為一些細節沒調出來,看了Sakits神犇的博客之後也沒明白自己掛在哪裏,於是就抄了個題解。然後現在突然想到填這個坑(其實是為了復習一下後綴數組模板),換了種思路才過了這道題……然而還是不知道當年那種寫法為什麽錯……似乎是掛在判重?

  解法(Accepted):

  首先我們可以觀察一下這個式子(見下),我們可以把它拆開,變成sigma(len(Ti)+len(Tj))-2*sigma(lcp(Ti,Tj))。其中前一項隨便在紙上算一算就能得出為n*(n+1)*(n-1)/2。

技術分享

  後面的lcp總和,我們可以先用後綴數組將其轉化為height數組的區間最小值之和。之前的寫法是統計以height數組每個值作為最小值的區間有多少個,再來算答案,然而似乎可能會有重復計算,要加一坨判斷。

  但是我們可以改變一下統計方式,統計以每個數作為右端點的區間的最小值總和,然後把他們加起來。這裏就要用到單調棧一個神奇的性質:把一個序列壓進單調棧,這個序列的後綴最小/最大值一定會在單調棧中出現。於是可以轉化為統計單調棧中每個數對最小值之和的貢獻。計算方法見下圖:

技術分享

此外,因為棧中每個數的貢獻從它被壓進棧到彈出是不會變的,於是只要在壓數和彈數時維護一下就能算出答案。

然後這道題就解決了^_^

代碼:

技術分享
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<algorithm>
#include<queue>
#include<vector>
#define ll long long
#define maxn 500010
inline ll read()
{
    ll tmp=0; char c=getchar(),f=1;
    while(c<
0||9<c){if(c==-)f=-1; c=getchar();} while(0<=c&&c<=9){tmp=tmp*10+c-0; c=getchar();} return tmp*f; } char s[maxn]; ll sa[maxn],rk[maxn],tsa[maxn],trk[maxn],sum[maxn],h[maxn]; ll st[maxn]; ll n; void getsa() { int i,tot,cnt=256,len; for(i=1;i<=cnt;i++)sum[i]=0; for(i=0;i<n;i++)trk[i+1]=s[i]+1,++sum[trk[i+1]]; for(i=2;i<=cnt;i++)sum[i]+=sum[i-1]; for(i=n;i;i--)sa[sum[trk[i]]--]=i; cnt=1; rk[sa[1]]=1; for(i=2;i<=n;i++){ if(trk[sa[i]]!=trk[sa[i-1]])++cnt; rk[sa[i]]=cnt; } for(len=1;cnt<n;len<<=1){ for(i=1;i<=n;i++)trk[i]=rk[i]; for(i=1;i<=n;i++)sum[i]=0; tot=0; for(i=n-len+1;i<=n;i++)tsa[++tot]=i; for(i=1;i<=n;i++)if(sa[i]>len)tsa[++tot]=sa[i]-len; for(i=1;i<=n;i++)rk[i]=trk[tsa[i]],++sum[rk[i]]; for(i=2;i<=cnt;i++)sum[i]+=sum[i-1]; for(i=n;i;i--)sa[sum[rk[i]]--]=tsa[i]; cnt=1; rk[sa[1]]=1; for(i=2;i<=n;i++){ if(trk[sa[i]]!=trk[sa[i-1]]||trk[sa[i]+len]!=trk[sa[i-1]+len])++cnt; rk[sa[i]]=cnt; } } for(i=1;i<=n;i++){ if(rk[i]==1)continue; if(h[rk[i-1]]>0)h[rk[i]]=h[rk[i-1]]-1;else h[rk[i]]=0; while(s[i+h[rk[i]]-1]==s[sa[rk[i]-1]+h[rk[i]]-1])++h[rk[i]]; } } int main() { int i; scanf("%s",s); n=strlen(s); getsa(); ll top=0,ans=(n-1)*n*(n+1)/2,tot=0; st[0]=0; h[0]=-(1<<30); for(i=1;i<=n;i++){ while(h[st[top]]>=h[i]){ tot-=h[st[top]]*(st[top]-st[top-1]); --top; } tot+=h[i]*(i-st[top]); st[++top]=i; ans-=2*tot; } printf("%lld",ans); }
bzoj3238

【bzoj3238】差異[AHOI2013](後綴數組+單調棧)