bzoj 3238: [AHOI2013]差异

                Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 3748  Solved: 1697
[Submit][Status][Discuss]

Description

一个长度为N的字符串S,求Σ(任意两个后缀的长度和-它们lcp长度的两倍)

Input

一行,一个字符串S

Output

 

一行,一个整数,表示所求值

Sample Input

cacao

Sample Output


54

HINT

 



2<=N<=500000,S由小写英文字母组成


我们知道光是后缀数组是没有什么用的,但是后缀数组可以用来求LCP,原理就是rmq。
求LCP的话需要在原基础上加一个height函数,代表后缀排序之后的数组的相邻位置的LCP。
这样的话求任意两个位置的LCP就变成了求一段区间的rmq。
求height可以做到O(N),需要用到一个玄学的单调性。。。。得按照在原序列中的位置计算。

然后小推一波式子题目就出来了(注意height[1]是0)。

 

#include<bits/stdc++.h>
#define ll long long
#define maxn 500005
using namespace std;
ll ans=0,tot=0;
char s[maxn];
int n,m,st[maxn],tp=0;
int sa[maxn],sax[maxn];
int sec[maxn],cc[maxn];
int rank[maxn<<1],rankx[maxn];
int height[maxn],now,k;

inline void get_height(){
    for(int i=0;i<n;i++) cc[s[i]]++;
    for(int i=1;i<=500;i++) cc[i]+=cc[i-1];
    for(int i=0;i<n;i++) sa[cc[s[i]]--]=i;
    for(int i=1;i<=n;i++){
        rank[sa[i]]=i;
        if(i>1&&s[sa[i]]==s[sa[i-1]]) rank[sa[i]]=rank[sa[i-1]];
    }
    
    int len=1;
    while(len<n){
        memset(cc,0,sizeof(cc));
        for(int i=0;i<n;i++) cc[sec[i]=rank[i+len]]++;
        for(int i=n-1;i>=0;i--) cc[i]+=cc[i+1];
        for(int i=0;i<n;i++) sax[cc[sec[i]]--]=i;
        
        memset(cc,0,sizeof(cc));
        for(int i=0;i<n;i++) cc[rank[i]]++;
        for(int i=1;i<=n;i++) cc[i]+=cc[i-1];
        for(int i=1;i<=n;i++) sa[cc[rank[sax[i]]]--]=sax[i];
        
        for(int i=1;i<=n;i++){
            rankx[sa[i]]=i;
            if(i>1&&rank[sa[i]]==rank[sa[i-1]]&&sec[sa[i]]==sec[sa[i-1]]) rankx[sa[i]]=rankx[sa[i-1]];
        }
        
        for(int i=0;i<n;i++) rank[i]=rankx[i];
        len<<=1;
    }
    
    int now=0,j,mx;
    for(int i=0;i<n;i++){
        if(rank[i]==1){
            now=0,height[1]=0;
            continue;
        }
        if(now) now--;
        
        j=sa[rank[i]-1];
        mx=max(j,i);
        while(mx+now<n&&s[i+now]==s[j+now]) now++;
        height[rank[i]]=now;
    }
}

inline void solve(){
    ans=n*(ll)(n+1)*(ll)(n-1)>>1ll;
    for(int i=1;i<=n;i++){
        while(tp&&height[st[tp]]>=height[i]) tp--;
        st[++tp]=i;
        for(int j=tp;j;j--) tot+=(ll)(st[j]-st[j-1])*(ll)height[st[j]];
    }
    ans-=tot<<1ll;
}

int main(){
    scanf("%s",s);
    n=strlen(s);
    get_height();
    solve();
    printf("%lld\n",ans);
    return 0;
}

 

posted @ 2018-01-13 07:33  蒟蒻JHY  阅读(606)  评论(5编辑  收藏  举报