Luogu5115 Check,Check,Check one two!

Luogu5115 Check,Check,Check one two!

重工业边分树不说了。

考虑对于一个\(i,j\)来说,\(\operatorname{lcp}(i,j),\operatorname{lcs}(i,j)\)有什么性质。

\[S(i-\operatorname{lcp}(i,j)+1,i+\operatorname{lcs}(i,j)-1)=S(j-\operatorname{lcp}(i,j)+1,j+\operatorname{lcs}(i,j)-1) \]

那么我们考虑两个相同的子串\(S(l_1,r_1),S(l_2,r_2)\),在什么情况下会对答案产生贡献呢?

\[S_{l1-1} \ne S_{l_2-1} \\ S_{r_1+1} \ne S_{r_2+1} \]

这是由于\(\operatorname{lcp},\operatorname{lcs}\)的限制所决定的。

在这个串中满足长度限制的位置都会产生贡献,可以利用计数公式\(O(1)\)计算预处理。

我们考虑在\(SAM\)上完成这个事情,我们把子串在\(parent\)\(LCA\)处统计所有前缀的贡献,那么统计贡献时左端点必然无法延伸。

然后是右端点,我们肯定选取的是当前节点代表的最长子串,然后试图在后面加字符。我们只需要记录所有前缀中后面可加入字符\(c\)的方案数,然后用总方案数减去所有可以添加相同字符的方案即可。

\(Code:\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 100005
#define ull unsigned long long
using namespace std;
int n,k1,k2;
int cnt=1,lst=1,tr[N << 1][26],len[N << 1],pre[N << 1];
int c[N],a[N << 1],sz[N << 1],num[N << 1][26];
char s[N];
ull ans=0,clc[N];
ull calc1(int n)
{
    return (ull)n*(n+1)/2;
}
ull calc2(int n)
{
    return (ull)n*(n+1)*(n+n+1)/6;
}
void preparation()
{
    for (int i=1;i<=n;++i)
    {
        int l=max(1,i-k2+1),r=min(i,k1);
        if (l<=r)
            clc[i]=(ull)(i+1)*(calc1(r)-calc1(l-1))-(calc2(r)-calc2(l-1));
    }
}
void ins(int c,int i)
{
    int p=lst,np=++cnt;
    lst=np,len[np]=len[p]+1,sz[np]=1;
    if (i!=n)
        num[np][s[i+1]-'a']=1;
    for (;p && !tr[p][c];p=pre[p])
        tr[p][c]=np;
    if (!p)
        pre[np]=1; else
        {
            int q=tr[p][c];
            if (len[p]+1==len[q])
                pre[np]=q; else
                {
                    int g=++cnt;
                    memcpy(tr[g],tr[q],sizeof(tr[q]));
                    len[g]=len[p]+1,pre[g]=pre[q];
                    for (;p && tr[p][c]==q;p=pre[p])
                        tr[p][c]=g;
                    pre[np]=pre[q]=g;
                }
        }
}
int main()
{
    scanf("%s",s+1),n=strlen(s+1);
    scanf("%d%d",&k1,&k2);
    preparation();
    for (int i=1;i<=n;++i)
        ins(s[i]-'a',i);
    for (int i=1;i<=cnt;++i)
        ++c[len[i]];
    for (int i=1;i<=n;++i)
        c[i]+=c[i-1];
    for (int i=cnt;i;--i)
        a[c[len[i]]--]=i;
    for (int i=cnt;i>=2;--i)
    {
        int u=a[i];
        ull o=(ull)sz[u]*sz[pre[u]];
        for (int j=0;j<26;++j)
            o-=(ull)num[u][j]*num[pre[u]][j];
        ans+=o*clc[len[pre[u]]];
        sz[pre[u]]+=sz[u];
        for (int j=0;j<26;++j)
            num[pre[u]][j]+=num[u][j];
    }
    printf("%llu\n",ans);
    return 0;
}
posted @ 2020-12-14 16:53  GK0328  阅读(98)  评论(0编辑  收藏  举报