Luogu6292 区间本质不同子串个数

Luogu6292 区间本质不同子串个数

\(SAM+LCT+Segment\_Tree\)

如果把一个子串看成一个元素,我们首先需要完成的是如何快速判断一个区间内的元素个数。

如果元素是数字,那么我们可以离线操作,枚举右端点\(r\),同时不断更新每个元素出现的位置,也就是每当一个元素出现,我们把它在原来位置的贡献减去,在新的位置加上,很容易证明这种方法能够做到不重不漏。

但是在字符串的子串中,元素个数可以达到\(n^2\)级别,暴力枚举显然不可行。

我们建立\(SAM\),然后观察询问的答案在\(SAM\)上上以怎样的形式呈现的。

我们需要维护每一个子串的最新出现的左端点,那么我们考虑,当增加一个字符\(S_r\)时,有多少个串会受到影响。

首先找到字符串\(r\)位置在\(SAM\)上的位置\(p\),显然这个位置包括了\(S[1\cdots r]\)这个最大的子串,那么同样以\(r\)为末位置的后缀必然是\(parent\)树上从\(p\)到根的路径,这条路径上所有点的集合中包含的串都是需要更新的(对于\(p\)节点,只有长度\(\le r\)的一部分被包含)。

我们需要简化运算,可以发现,对于一个节点所包含的集合,由于它们是等价的,因此它们的末位置相同,而长度连续,因此我们可以在线段树上区间修改。

但是这样仍然需要暴力跳\(parent\)树,最坏时间复杂度\(O(nq \log n)\)

可以发现,我们进行的操作是很特殊的,也就是把一个节点\(p\)到根\(1\)的路径赋值为同一个,树上链赋新值,总共会产生的颜色段数为\(n \log n\),如何证明,显然这个操作就是\(LCT\)\(access\)操作,对于链赋新值,可以看一下Luogu3703 [SDOI2017]树点涂色,这题的\(LCT\)解法与本题十分类似。

也就是说,我们总共需要修改的异色颜色段数总共为\(n \log n\)个,而我们却花了\(n^2\)的时间去求解它。

我们考虑,同色颜色段代表着什么,这意味着它们的左端点是相同的,而同色颜色段以树上链的形式分布,这条链代表的子串是连续的,因此我们可以把整条链抽出来进行区间减操作。

因此我们维护\(LCT\),在同一个\(splay\)中节点正代表一个同色颜色段。

同时\(access\)有一个性质,除了第一个节点以外,其他访问的节点都是从询问节点到根的路径在\(splay\)中最深的节点(比它更深的一定不在访问的链上),因此覆盖的区间为\([r_x-len_x+1,r_x-len_{fa_x}]\)\(fa_x\)\(LCT\)上的父亲),然后特判\(1\)号节点即可。

自此,我们已经在\(O(n \log^2 n)\)的时间内完成了这道看似极其困难的题目。

\(Code:\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#define ll long long
#define rint register int
#define il inline
#define pr pair<int,int>
#define mp make_pair
#define IT vector< pr > :: iterator
#define N 100005
#define M 200005
using namespace std;
int n,m,l,r;
int cnt=1,lst=1,t[N << 1][26],pre[N << 1],len[N << 1];
char s[N];
vector< pr >e[N];
ll ans[M];
il int read()
{
    int s=0;
    char c=getchar();
    while (c<'0' || c>'9')
        c=getchar();
    while ('0'<=c && c<='9')
        s=(s << 3)+(s << 1)+(c^48),c=getchar();
    return s;
}
void write(ll x)
{
    if (x>9)
        write(x/10);
    putchar(x%10+48);
}
il void ins(int c)
{
    int p=lst,q,np=++cnt;
    len[np]=len[p]+1;
    lst=np;
    for (;p && !t[p][c];p=pre[p])
        t[p][c]=np;
    if (!p)
        pre[np]=1; else
        {
            int q=t[p][c];
            if (len[p]+1==len[q])
                pre[np]=q; else
                {
                    int g=++cnt;
                    memcpy(t[g],t[q],sizeof(t[q]));
                    len[g]=len[p]+1,pre[g]=pre[q];
                    for (;p && t[p][c]==q;p=pre[p])
                        t[p][c]=g;
                    pre[np]=pre[q]=g;
                }
        }
}
struct node
{
    ll s;
    int l,r,tag;
}tr[N << 2];
il void push_tag(int p,int tag)
{
    if (!p)
        return;
    tr[p].s+=(ll)tag*(tr[p].r-tr[p].l+1);
    tr[p].tag+=tag;
}
il void push_down(int p)
{
    if (tr[p].tag)
    {
        push_tag(p << 1,tr[p].tag);
        push_tag(p << 1 | 1,tr[p].tag);
        tr[p].tag=0;
    }
}
il void update(int p)
{
    tr[p].s=tr[p << 1].s+tr[p << 1 | 1].s;
}
void build(int p,int l,int r)
{
    tr[p].l=l,tr[p].r=r;
    if (l==r)
        return;
    int mid=(l+r) >> 1;
    build(p << 1,l,mid);
    build(p << 1 | 1,mid+1,r);
}
void modify(int p,int l,int r,int x,int y,int z)
{
    if (l==x && r==y)
    {
        push_tag(p,z);
        return;
    }
    push_down(p);
    int mid=(l+r) >> 1;
    if (y<=mid)
        modify(p << 1,l,mid,x,y,z); else
    if (x>mid)
        modify(p << 1 | 1,mid+1,r,x,y,z); else
        {
            modify(p << 1,l,mid,x,mid,z);
            modify(p << 1 | 1,mid+1,r,mid+1,y,z);
        }
    update(p);
}
ll calc(int p,int l,int r,int x,int y)
{
    if (l==x && r==y)
        return tr[p].s;
    push_down(p);
    int mid=(l+r) >> 1;
    if (y<=mid)
        return calc(p << 1,l,mid,x,y); else
    if (x>mid)
        return calc(p << 1 | 1,mid+1,r,x,y); else
        return calc(p << 1,l,mid,x,mid)+calc(p << 1 | 1,mid+1,r,mid+1,y);
}
#define ls(x) a[x].ch[0]
#define rs(x) a[x].ch[1]
#define fa(x) a[x].f
#define ts(x) a[x].stg
#define tg(x) a[x].tag
struct LCT
{
    int ch[2],f;
    int stg=0,tag=0;
}a[N << 1];
int q[N << 1];
il void push_g(int x,int z)
{
    if (!x)
        return;
    ts(x)=tg(x)=z;
}
il void push_d(int x)
{
    if (tg(x))
    {
        push_g(ls(x),tg(x));
        push_g(rs(x),tg(x));
        tg(x)=0;
    }
}
il bool isrt(int x)
{
    return ls(fa(x))!=x && rs(fa(x))!=x;
}
il int id(int x)
{
    return ls(fa(x))==x?0:1;
}
il void connect(int x,int F,int son)
{
    fa(x)=F;
    a[F].ch[son]=x;
}
il void rot(int x)
{
    int y=fa(x),r=fa(y);
    int yson=id(x),rson=id(y);
    if (isrt(y))
        fa(x)=r; else
        connect(x,r,rson);
    connect(a[x].ch[yson^1],y,yson);
    connect(y,x,yson^1);
}
il void splay(int x)
{
    int g=x,k=0;
    q[++k]=g;
    while (!isrt(g))
        g=fa(g),q[++k]=g;
    while (k)
        push_d(q[k--]);
    while (!isrt(x))
    {
        int y=fa(x);
        if (isrt(y))
            rot(x); else
        if (id(x)==id(y))
            rot(y),rot(x); else
            rot(x),rot(x);
    }
}
il void access(int x,int r)
{
    int y;
    for (y=0;x;y=x,x=fa(x))
    {
        splay(x);
        if (x!=1 && ts(x))
            modify(1,1,n,ts(x)-len[x]+1,ts(x)-len[fa(x)],-1);
        rs(x)=y;
    }
    push_g(y,r);
    modify(1,1,n,1,r,1);
}
int main()
{
    scanf("%s",s+1);
    n=strlen(s+1);
    for (rint i=1;i<=n;++i)
        ins(s[i]-'a');
    m=read();
    for (rint i=1;i<=m;++i)
        l=read(),r=read(),e[r].push_back(mp(l,i));
    build(1,1,n);
    for (rint i=2;i<=cnt;++i)
        fa(i)=pre[i];
    int st=1;
    for (rint i=1;i<=n;++i)
    {
        st=t[st][s[i]-'a'];
        access(st,i);
        for (IT it=e[i].begin();it!=e[i].end();++it)
            ans[it->second]=calc(1,1,n,it->first,i);
    }
    for (rint i=1;i<=m;++i)
        write(ans[i]),putchar('\n');
    return 0;
}
posted @ 2020-11-11 19:47  GK0328  阅读(315)  评论(2编辑  收藏  举报