【jzoj5084】【GDOI2017第四轮模拟day1】【子串】【后缀数组】

23 篇文章 1 订阅

题目大意

YJC最近写了一篇关于子串的论文。CJY看他那么喜欢子串,决定出一道题考考他。
CJY给出了一个字符串s,令s[l~r]表示s中区间[l,r]构成的子串,下标从1开始。他向YJC提出了许多询问,每次询问给了四个数i,j,k,l,求LCP(s[i~k],s[j~l]),即每次询问两个子串的最长公共前缀。YJC很轻松地解决了这个问题。CJY表示很不爽,于是他改变了一下问题,让YJC求所有合法的询问的答案的和,即设n=|S|,求这里写图片描述
。YJC发现现在自己不会做了,于是他来向你求助。因为这个数可能很大,你只需要告诉他答案mod 998244353的值即可。

解题思路

先做一遍sa,对height前后做一次单调栈,分别是递增和不递减,求出点的数量和sa和,推出公式后可以直接得解,先把含 的式子求出来,用等差数列求和公式前缀平方和公式求和即可。注意把答案乘二后加上自己乘自己的情况。

code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define fo(i,j,k) for(LL i=j;i<=k;i++)
#define fd(i,j,k) for(LL i=j;i>=k;i--)
using namespace std;
LL const mn=5*1e5+9,mo=998244353;
LL n,m,a[2][mn],sa[mn],cnt[mn],h[mn],st[mn],f[mn],g[mn],suml[mn],sizel[mn];
LL *rank=a[0],*ord=a[1];
char s[mn];
void sort(){
    fo(i,1,m)cnt[i]=0;
    fo(i,1,n)cnt[rank[i]]++;
    fo(i,1,n)cnt[i]+=cnt[i-1];
    fd(i,n,1)sa[cnt[rank[ord[i]]]--]=ord[i];
}
LL diff(LL x,LL y,LL z){
    return (ord[x]!=ord[y])||(ord[x+z]!=ord[y+z]);
}
int main(){
    //freopen("substring.in","r",stdin);
    //freopen("substring.out","w",stdout);
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    scanf("%s",s+1);n=strlen(s+1);
    fo(i,1,n)rank[i]=s[i]-'a'+1,ord[i]=i;m=26;
    sort();
    for(LL w=1,p=0;p!=n;w<<=1){
        p=0;fo(i,n-w+1,n)ord[++p]=i;
        fo(i,1,n)if(sa[i]>w)ord[++p]=sa[i]-w;
        sort();swap(rank,ord);rank[sa[1]]=p=1;
        fo(i,2,n)rank[sa[i]]=p+=diff(sa[i],sa[i-1],w);
        m=p;
    }
    for(LL i=1,j,p=0;i<=n;h[rank[i]]=p,i++)
        for(j=sa[rank[i]-1],p=(p)?p-1:p;s[i+p]==s[j+p];p++);
    LL ans=0;
    fo(i,2,n){
        LL tmp=0,tmp2=0;
        while(st[0]&&(st[st[0]]>h[i]))tmp+=f[st[0]],tmp2+=g[st[0]],st[0]--;
        st[++st[0]]=h[i];
        suml[i]=f[st[0]]=(tmp+sa[i-1])%mo;
        sizel[i]=g[st[0]]=tmp2+1;
    }
    st[0]=0;
    fd(i,n,2){
        LL tmp=0,tmp2=0;
        while(st[0]&&(st[st[0]]>=h[i]))tmp+=f[st[0]],tmp2+=g[st[0]],st[0]--;
        st[++st[0]]=h[i];
        f[st[0]]=(tmp+sa[i])%mo;
        g[st[0]]=tmp2+1;
        LL l=h[i],sumr=f[st[0]],sizer=g[st[0]];
        ans=(ans+((l*(l+1)/2-l*(l+1)*(2*l+1)/6)/2+l*(n+1-l)*(n+1-l)-l*l*(l+1)/2)%mo*(sizel[i]*sizer%mo)
            -(l*(n+1-l)%mo)*((suml[i]*sizer+sumr*sizel[i])%mo)+(l*(l+1)/2%mo)*((((n+1)*sizel[i]-suml[i])*sizer+((n+1)*sizer-sumr)*sizel[i])%mo)
            +l*suml[i]%mo*sumr)%mo;
    }
    ans<<=1;
    fo(i,1,n)ans=(ans+(i*(i+1)/2-i*(i+1)*(2*i+1)/6)/2+i*i*(i+1)/2)%mo;
    printf("%lld",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值