# P4248 [AHOI2013]差異 (後綴數組+單調棧)

P4248 [AHOI2013]差異 (後綴數組+單調棧)

題目鏈接:傳送門

正文:

首先對於這個公式的前兩項我們可以快速求出,爲(1+n)n(n1)/2(1+n)*n*(n-1)/2。所以我們只需考慮最後一項的和,又因爲每個TiT_i對應排序後的後綴的rank[i]rank[i]位置,所以這步可以轉化爲在排序後的後綴數組上統計答案。

首先最後一項lcp(Ti,Tj)lcp(T_i,T_j) 的值,設x=rank[i],y=rank[j]x=rank[i],y=rank[j],不訪假設x<yx<y,那麼lcp(Ti,Tj)lcp(T_i,T_j)等於min{height[x+1],height[x+2]....,height[y]}min\{height[x+1],height[x+2]....,height[y]\},即區間[x+1,y][x+1,y]height[]height[]數組中的最小的值height[k]height[k] 的值是貢獻。如果最小高度有多個,那麼對規定最左邊的最小高度起貢獻。

因爲最後一項的是所有元組,所以我們可以用單調棧維護每個height[]height[]貢獻的區間即可,

代碼:

#include<bits/stdc++.h>
#define mset(a,b)   memset(a,b,sizeof(a))
typedef long long ll;
const int N=5e5+10;//下標從1開始
int rak[N],sa[N],tp[N],c[N],height[N];
//最後的rak[i]=p 與sa[p]=i ,11對應
void SA(char *s,int n)//這個基數排序的版本中桶的大小爲第一關鍵詞rak的最大值
{
    int m=128;//桶的大小,會慢慢變大,最大爲n,但初始時是字符的最大值
    //首先基數排序初始化rak數組和sa數組
    //第一輪基數排序,如果s的最大值很大,可改用快速排序
    for(int i=0; i<=m; ++i) c[i]=0;
    for(int i=1; i<=n; ++i) c[rak[i]=s[i]]++;
    for(int i=1; i<=m; ++i) c[i]+=c[i-1];
    for(int i=n; i>=1; --i) sa[c[s[i]]--]=i; //所有後綴長度爲1的字符串已求出

    for(int k=1,p ; k < n; k<<=1 ) //p是一個計數器,現在還沒用。
    {
        /*tp[i]:第二關鍵詞排名爲i的位置爲tp[i]*/
        p=0;
        for(int i=n-k+1; i<=n; ++i) tp[++p]=i;
        for(int i=1; i<=n; ++i) if(sa[i]>k) tp[++p]=sa[i]-k;
        /*基數排序求出2k長度的sa數組*/
        for(int i=0; i<=m; ++i) c[i]=0;
        for(int i=1; i<=n; ++i) c[rak[i]]++;
        for(int i=1; i<=m; ++i) c[i]+=c[i-1];
        for(int i=n; i>=1; --i) sa[ c[rak[tp[i]]]-- ]=tp[i];
        /*求該輪的rank數組*/
        //tp纔是上一輪的rak,現在要利用上輪的rak和這輪的sa求這輪的rank
        std::swap(rak,tp);
        rak[sa[1]]=p=1;
        for(int i=2,a,b; i<=n; ++i)
        {
            a=sa[i],b=sa[i-1];
            if(tp[a]==tp[b]&&((a+k<=n&& b+k<=n&&tp[a+k]==tp[b+k])||(a+k > n&& b+k > n))) rak[a]=p;
            else rak[a]=++p;
        }
        if(p>=n) break;//可以提前退出
        m=p;
    }//計算高度
    int k=0;
    for(int i=1; i<=n; ++i)
    {
        if(k) k--;//i  ,
        int j=sa[rak[i]-1];
        if(j==0) continue;
        while(s[j+k]==s[i+k]) ++k;
        height[rak[i]]=k;
    }
}
char s[N];
int S[N],top;
int left[N],right[N];
int main()
{
    scanf("%s",s+1);
    int ls=strlen(s+1);
    SA(s,ls);
    top=0;
    for(int i=2;i<=ls;++i)
    {
        while(top > 0&& height[S[top]]> height[i]) top--;
        S[++top]=i;
        if(top==1) left[i]=2;
        else left[i]=S[top-1]+1;
    }
    top=0;
    for(int i=ls;i>=2;--i)
    {
        while(top>0 && height[S[top]] >=height[i]) top--;
        S[++top]=i;
        if(top==1) right[i]=ls;
        else right[i]=S[top-1]-1;
    }
    long long ans=(1ll+ls)*(ls-1)*(ls)/2ll;
    for(int i=2;i<=ls;++i)
    {
        ans-=2ll*height[i]*(right[i]-i+1)*(i-left[i]+1);
    }
    printf("%lld\n",ans);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章