• [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;
        }
    }
    
    
  • 相关阅读:
    AcWing 286. 选课
    背包问题极小值空间至少是j
    Logo在线公式编辑转图片
    AcWing 1055. 股票买卖 II
    枚举一个集合的所有有效子集
    CF580D Kefa and Dishes [洛谷]
    AcWing 1282. 搜索关键词
    背包问题最大值空间恰好是j
    多叉树转二叉树有依赖的背包问题解法
    Thinkpad T14重装Win10后不见无线网络问题的解决
  • 原文地址:https://www.cnblogs.com/mollnn/p/13619686.html
Copyright © 2020-2023  润新知