[P6292] 区间本质不同子串个数

Description

给定一个长度为 (n) 的字符串 (S),有 (m) 个询问,每次询问给定 (l,r),求 (S[l..r]) 中有多少个本质不同的子串。

Solution

考虑离线,将所有询问挂在右端点上,从左到右扫描整个序列,设当前扫描到的位置为 (r)

用线段树统计答案,第 (i) 位表示 (S[1..r]) 中最后一次出现时左端点为 (i) 的子串数目,于是询问就是求 ([l,r]) 的和

考虑修改,当新增字符 (S[r]) 时会对 ([1,r]) 各产生 (1) 的贡献,需要去重

用 SAM 维护串,则从 (S[1..r]) 对应节点 (p_0) 沿着后缀链接跳,经过的所有结点上一次出现的右端点 (lastpos) 便能计算出需要在线段树上 (-1) 的区间 ([lastpos[p]-len[p]+1,lastpos[p]-len[fa[p]]]),跳完后还要把这些节点的 (lastpos) 都设置为 (p)

这样做非常慢,考虑到 (lastpos) 相同的点可以放在一起处理,每次从 (p_0) 跳到根的过程相当于把这条链并起来的过程,不难想到用 LCT 的 Access 操作来维护

每次处理相当于将根到 (p_0) 的路径 Access 一下,在 Access 的过程中顺便统计答案并修改 (lastpos)

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 1000005;

int n,m;

struct segseg
{
    int val[N],tag[N];
    void put(int p,int l,int r,int v)
    {
        tag[p]+=v;
        val[p]+=(r-l+1)*v;
    }
    void pushup(int p)
    {
        val[p]=val[p*2]+val[p*2+1];
    }
    void pushdown(int p,int l,int r)
    {
        if(tag[p])
        {
            put(p*2,l,(l+r)/2,tag[p]);
            put(p*2+1,(l+r)/2+1,r,tag[p]);
            tag[p]=0;
        }
    }
    void modify(int p,int l,int r,int ql,int qr,int v)
    {
        if(l>qr || r<ql) return;
        if(l>=ql&&r<=qr)
        {
            put(p,l,r,v);
        }
        else
        {
            pushdown(p,l,r);
            modify(p*2,l,(l+r)/2,ql,qr,v);
            modify(p*2+1,(l+r)/2+1,r,ql,qr,v);
            pushup(p);
        }
    }
    int query(int p,int l,int r,int ql,int qr)
    {
        if(l>qr || r<ql) return 0;
        if(l>=ql&&r<=qr) return val[p];
        pushdown(p,l,r);
        return query(p*2,l,(l+r)/2,ql,qr) + query(p*2+1,(l+r)/2+1,r,ql,qr);
    }
} seg;

struct SAM
{
    int len[N], ch[N][26], fa[N], ind, last;
    int t[N], a[N], cnt[N], f[N];
    SAM()
    {
        ind = last = 1;
    }
    void extend(int id)
    {
        int cur = (++ ind), p;
        len[cur] = len[last] + 1;
        cnt[cur] = 1;
        for (p = last; p && !ch[p][id]; p = fa[p]) ch[p][id] = cur;
        if (!p) fa[cur] = 1;
        else
        {
            int q = ch[p][id];
            if (len[q] == len[p] + 1) fa[cur] = q;
            else
            {
                int tmp = (++ ind);
                len[tmp] = len[p] + 1;
                for(int i=0; i<26; i++) ch[tmp][i] = ch[q][i];
                fa[tmp] = fa[q];
                for (; p && ch[p][id] == q; p = fa[p]) ch[p][id] = tmp;
                fa[cur] = fa[q] = tmp;
            }
        }
        last = cur;
    }
} sam;


struct lctlct
{
    int top, q[N], ch[N][2], fa[N], rev[N];
    int a[N], tag[N], siz[N], val[N];
    inline void pushup(int x)
    {
    }
    void put(int p,int v)
    {
        if(!p) return;
        tag[p]=val[p]=v;
    }
    inline void pushdown(int x)
    {
        if(rev[x])
        {
            rev[ch[x][0]]^=1;
            rev[ch[x][1]]^=1;
            rev[x]^=1;
            swap(ch[x][0],ch[x][1]);
        }
        if(tag[x])
        {
            put(ch[x][0],tag[x]);
            put(ch[x][1],tag[x]);
            tag[x]=0;
        }
    }
    inline bool isroot(int x)
    {
        return ch[fa[x]][0]!=x && ch[fa[x]][1]!=x;
    }
    inline void rotate(int p)
    {
        int q=fa[p], y=fa[q], x=ch[fa[p]][1]==p;
        ch[q][x]=ch[p][x^1];
        fa[ch[q][x]]=q;
        ch[p][x^1]=q;
        fa[q]=p;
        fa[p]=y;
        if(y) if(ch[y][0]==q) ch[y][0]=p;
            else  if(ch[y][1]==q) ch[y][1]=p;
        pushup(q);
        pushup(p);
    }
    inline void splay(int x)
    {
        q[top=1]=x;
        for(int i=x; !isroot(i); i=fa[i]) q[++top]=fa[i];
        for(int i=top; i; i--) pushdown(q[i]);
        for(; !isroot(x); rotate(x))
            if(!isroot(fa[x]))
                rotate((ch[fa[x]][0]==x)==(ch[fa[fa[x]]][0]==fa[x])?fa[x]:x);
    }
    void access(int x,int k)
    {
        int y=0;
        for(;x;y=x,x=fa[x])
        {
            splay(x);
            if(val[x])
            {
                //cout<<val[x]-sam.len[x]+1<<" "<<val[x]-sam.len[fa[x]]<<endl;
                seg.modify(1,1,n,val[x]-sam.len[x]+1,val[x]-sam.len[fa[x]],-1);
            }
            ch[x][1]=y;
        }
        put(y,k);
        seg.modify(1,1,n,1,k,1);
    }
} lct;

struct request
{
    int id,l;
};

vector <request> req[N];
char s[N];
int ans[N],pos[N];

signed main()
{
    ios::sync_with_stdio(false);

    cin>>(s+1);
    n=strlen(s+1);
    for(int i=1;i<=n;i++)
    {
        sam.extend(s[i]-'a');
        pos[i]=sam.last;
    }

    for(int i=2;i<=sam.ind;i++)
    {
        lct.fa[i]=sam.fa[i];
    }

    cin>>m;
    for(int i=1;i<=m;i++)
    {
        int l,r;
        cin>>l>>r;
        req[r].push_back({i,l});
    }

    for(int i=1;i<=n;i++)
    {
        lct.access(pos[i],i);
        for(request q:req[i])
        {
            ans[q.id]=seg.query(1,1,n,q.l,i);
        }
    }

    for(int i=1;i<=m;i++)
    {
        cout<<ans[i]<<endl;
    }
}

原文地址:https://www.cnblogs.com/mollnn/p/13619686.html