[bzoj3238][Ahoi2013]差异

来自FallDream的博客,未经允许,请勿转载,谢谢。


n<=500000

我只会yy一个奇怪做法.....

因为lcp是后缀数组中一段区间的Height的最小值,所以考虑先求出Height数组,然后对于每一位,二分它能成为最小值的区间并计算答案。

当然,也可以直接建出小顶的笛卡尔树,满足堆性质的同时顺序不变,这样笛卡尔树上每个节点的贡献就是2*h[x]*(size(l[x])+1)*(size(r[x])+1)

怎么感觉我的后缀数组特别慢啊?

后缀数组+二分

#include<iostream>
#include<cstdio>
#include<cstring>
#define INF 2000000000
#define MN 500000
#define MD 19
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

long long ans=0;
char st[MN*2+5];
int n,v[MN+5],sa[2][MN+5],P[MN+5],rk[2][MN+5],k,p=1,q=0,h[MN+5],f[MD+1][MN+5],g[MD+1][MN+5],Fa[MD+1][MN+5],fa[MD+1][MN+5];

void Calc(int*sa,int*rk,int*SA,int*Rk)
{
    for(int i=1;i<=n;i++) v[rk[sa[i]]]=i;
    for(int i=n;i;--i) 
        if(sa[i]>k) SA[v[rk[sa[i]-k]]--]=sa[i]-k;
    for(int i=n-k+1;i<=n;i++) SA[v[rk[i]]--]=i;
    for(int i=1;i<=n;i++)
        Rk[SA[i]]=Rk[SA[i-1]]+(rk[SA[i]]!=rk[SA[i-1]]||rk[SA[i]+k]!=rk[SA[i-1]+k]);
}

void GetH(int*sa,int*rk)
{
    for(int i=1,k=0;i<=n;h[rk[i++]]=k,k=max(0,k-1))
        for(int j=sa[rk[i]-1];st[j+k]==st[i+k];++k);    
}

inline int Calc(int l,int r)
{    
    if(l>r) return -1;
    int len=P[r-l+1];    
    return min(f[len][l],g[len][r]);    
}

int GetLeft(int l,int r,int x)
{
    int mid,ans=0;    
    while(l<=r)
    {
        mid=l+r>>1;
        if(Calc(mid,x)>=h[x]) ans=mid,r=mid-1;
        else l=mid+1;    
    }
    return ans;
}

int GetRight(int l,int r,int x)
{
    int mid,ans=0;
    while(l<=r)
    {
        mid=l+r>>1;
        if(Calc(x+1,mid)>h[x]) ans=mid,l=mid+1;
        else r=mid-1;
    }
    return ans;
}

int main()
{
    scanf("%s",st+1);n=strlen(st+1);
    ans=1LL*n*(n+1)/2*(n-1);
    for(int i=1;i<=n;i++) ++v[st[i]-'a'];
    for(int i=1;i<26;i++) v[i]+=v[i-1];
    for(int i=1;i<=n;i++) sa[0][v[st[i]-'a']--]=i; 
    for(int i=1;i<=n;i++) rk[0][sa[0][i]]=rk[0][sa[0][i-1]]+(st[sa[0][i]]!=st[sa[0][i-1]]);
    for(k=1;k<n;k<<=1) 
    {
        Calc(sa[q],rk[q],sa[p],rk[p]);
        swap(p,q);
    }
    GetH(sa[q],rk[q]); h[1]=INF; 
    memset(g,42,sizeof(g));memset(f,42,sizeof(f));
    for(int i=1;i<=n;i++) g[0][i]=f[0][i]=h[i],fa[0][i]=i+1,Fa[0][i]=i-1;
    fa[0][n]=0;P[0]=-1;
    for(int i=1;i<=n;i++) P[i]=P[i>>1]+1;
    for(int i=1;i<=MD;i++)
        for(int j=1;j<=n;j++)
            fa[i][j]=fa[i-1][fa[i-1][j]],
            f[i][j]=min(f[i-1][j],f[i-1][fa[i-1][j]]),
            Fa[i][j]=Fa[i-1][Fa[i-1][j]],
            g[i][j]=min(g[i-1][j],g[i-1][Fa[i-1][j]]);
    for(int i=2;i<=n;i++)
    {
        int lt,rt;
        (lt=GetLeft(2,i-1,i))?0:lt=i;
        (rt=GetRight(i+1,n,i))?0:rt=i;
        ans-=2LL*(i-lt+1)*(rt-i+1)*h[i];
    }
    printf("%lld\n",ans);
    return 0;
}
View Code

后缀数组+笛卡尔树

#include<iostream>
#include<cstdio>
#include<cstring>
#define MN 500000
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

long long ans=0;
char st[MN*2+5];
int n,v[MN+5],sa[2][MN+5],rk[2][MN+5],k,p=1,q=0,h[MN+5],Q[MN+5],top=0,size[MN+5],l[MN+5],r[MN+5];

void Calc(int*sa,int*rk,int*SA,int*Rk)
{
    for(int i=1;i<=n;i++) v[rk[sa[i]]]=i;
    for(int i=n;i;--i) 
        if(sa[i]>k) SA[v[rk[sa[i]-k]]--]=sa[i]-k;
    for(int i=n-k+1;i<=n;i++) SA[v[rk[i]]--]=i;
    for(int i=1;i<=n;i++)
        Rk[SA[i]]=Rk[SA[i-1]]+(rk[SA[i]]!=rk[SA[i-1]]||rk[SA[i]+k]!=rk[SA[i-1]+k]);
}

void GetH(int*sa,int*rk)
{
    for(int i=1,k=0;i<=n;h[rk[i++]]=k,k=max(0,k-1)) if(rk[i]!=1)
        for(int j=sa[rk[i]-1];st[j+k]==st[i+k];++k);    
}

int build()
{
    int last;
    for(int i=2;i<=n;i++)
    {
        last=0;
        while(top&&h[Q[top]]>=h[i]) last=Q[top--];
        if(top) r[Q[top]]=i;
        Q[++top]=i;
        l[Q[top]]=last; 
    }
    for(;top>1;--top) r[Q[top-1]]=Q[top];
    return Q[1];
}

void dfs(int x)
{
    if(l[x]) dfs(l[x]);
    if(r[x]) dfs(r[x]);
    size[x]=size[l[x]]+size[r[x]]+1;
    ans-=2LL*h[x]*(size[l[x]]+1)*(size[r[x]]+1); 
}

int main()
{
    scanf("%s",st+1);n=strlen(st+1);
    ans=1LL*n*(n+1)/2*(n-1);
    for(int i=1;i<=n;i++) ++v[st[i]-'a'];
    for(int i=1;i<26;i++) v[i]+=v[i-1];
    for(int i=1;i<=n;i++) sa[0][v[st[i]-'a']--]=i; 
    for(int i=1;i<=n;i++) rk[0][sa[0][i]]=rk[0][sa[0][i-1]]+(st[sa[0][i]]!=st[sa[0][i-1]]);
    for(k=1;k<n;k<<=1) 
    {
        Calc(sa[q],rk[q],sa[p],rk[p]);
        swap(p,q);
    }
    GetH(sa[q],rk[q]); 
    int rt=build();
    dfs(rt);
    printf("%lld\n",ans);
    return 0;
}
View Code

 

posted @ 2017-04-24 18:34  FallDream  阅读(218)  评论(0编辑  收藏  举报