3238: [Ahoi2013]差异 后缀数组+分治+乘法原理

暴力水过了。。
st表预处理出最小值点,每次寻找最小值点然后乘法原理计算两边的乘积。再对左右区间分别处理。6s妥妥水过。

卧槽我后缀数组居然写错了。。调了一早晨都以为是分治写错。。果然不能对自己的后缀数组太自信QAQ。

#include<bits/stdc++.h>
#define inf 1000000007
#define ll long long 
#define N 500005
using namespace std;
char s[500005];
int len;
ll ans,sum;
int t1[N],t2[N],cc[N],sa[N],rank[N],height[N];
struct node {int v,id;} f[N][20];
inline bool cmp(int *y,int a,int b,int l)
{
    int arank1=y[a];
    int brank1=y[b];
    int arank2=a+l>=len?-1:y[a+l];
    int brank2=b+l>=len?-1:y[b+l];
    return arank1==brank1&&arank2==brank2;
}
inline void make_sa()
{
    int *x=t1,*y=t2;
    int m=26;
    for (int i=0;i<m;i++) cc[i]=0;
    for (int i=0;i<len;i++) ++cc[x[i]=s[i]-'a'];
    for (int i=1;i<m;i++) cc[i]+=cc[i-1];
    for (int i=len-1;~i;i--) sa[--cc[x[i]]]=i;
    for (int k=1;k<len;k<<=1)
    {
        int p=0;
        for (int i=len-k;i<len;i++) y[p++]=i;
        for (int i=0;i<len;i++)
            if (sa[i]>=k) y[p++]=sa[i]-k;
        for (int i=0;i<m;i++) cc[i]=0;
        for (int i=0;i<len;i++) ++cc[x[y[i]]];
        for (int i=1;i<m;i++) cc[i]+=cc[i-1];
        for (int i=len-1;~i;i--) sa[--cc[x[y[i]]]]=y[i];
        swap(x,y); 
        m=1; x[sa[0]]=0;
        for (int i=1;i<len;i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?m-1:m++;
        if (m>=len) break;
    }
}
inline void make_height()
{
    for (int i=0;i<len;i++) rank[sa[i]]=i;
    int k=0; height[0]=0;
    for (int i=0;i<len;i++)
    {
        if (!rank[i]) continue;
        int j=sa[rank[i]-1];
        if (k) k--;
        while (s[j+k]==s[i+k]) k++;
        height[rank[i]]=k;
    }
}
inline void make_st()
{
    for (int i=1;i<len;i++) f[i][0]=(node){height[i],i}; f[0][0]=(node){inf,0};
    for (int j=1;j<=19;j++)
        for (int i=0;i<len;i++)
            if (i+(1<<j)-1<len) 
            {
                f[i][j]=f[i][j-1];
                if (f[i][j].v>f[i+(1<<(j-1))][j-1].v)
                    f[i][j]=f[i+(1<<(j-1))][j-1];
            }
}
inline node ask_min(int l,int r)
{
    int k=log2(r-l+1);
    if (f[l][k].v<f[r-(1<<k)+1][k].v) return f[l][k]; else return f[r-(1<<k)+1][k];
}
void dfs(int l,int r)
{   
    if (l>r) return;
    if (l==r) {sum+=(ll)height[l]; return;}
    node t=ask_min(l,r);
    sum+=(ll)(t.id-l+1)*(r-t.id+1)*t.v;
    dfs(l,t.id-1); dfs(t.id+1,r);
}
int main()
{
    scanf("%s",s);
    len=strlen(s);
    ans=(ll)len*(len-1)*(len+1)/2;
    make_sa();
    make_height();
    make_st();
    dfs(1,len-1);
    cout << ans-2*sum;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值