P6292 区间本质不同子串个数 SAM+LCT+线段树

技术文章 6个月前 完美者
1,311 0

标签:space   ble   max   const   ++   auto   cond   维护   根据   

题意:

戳这里

分析:

  • 前置芝士:SAM(求本质不同的子串数目),LCT (在SAM上动态修改)线段树

首先我们先考虑求区间内元素种类数 这类问题的常见做法,就是对于每一个元素只维护它最后一次出现的位置,然后区间查询和值就可以了,但为了实现这个操作,我们必须找到一个方法求出本质相同的子串上一次出现的位置,对于一个串 \(S\) 我们记它右端点最后一次出现的位置在 \(lst\) 这样左端点扫到 \([1,lst-|S|+1]\) 时贡献 \(+1\)

对于这种本质不同子串的问题我们建出 \(SAM\),考虑右移一个位置带来的影响,假设右移到 \(i\) 这个位置,所有以 \(i\) 结尾的子串,就是前缀 \(i\) 对应的节点在 \(parent\) 树上的祖先,所以我们可以暴力跳祖先将这些节点对应的子串最后一次出现的位置改为 \(i\) ,对于区间 \([1,lst-|s|+1]\) 整体加 \(1\), 但是这样的复杂度不太对劲

我们继续在 \(parent\) 瞎搞 思考,由于所有以 \(i\) 结尾的子串的长度是连续的,所以我们只需要将 \(parent\) 树上某一点到根的路径上所有的串的 \(lst\) 改为 \(i\) 就行了,由于我们查询的时候是根据左端点扫描到的个数统计答案,所以我们需要将这些 \(lst\)\(i\) 的串的左端点改为一段连续的区间,并对这些连续的区间,每一个区间整体+\(1\),相当于我们需要加一个等差数列,对于区间加等差数列,单点查询转化为区间加差分数列,区间查询这样直接上线段树就可以了

代码:

#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk(x,y) make_pair(x,y)
#define lc rt<<1
#define rc rt<<1|1
#define pb push_back
#define fir first
#define sec second

using namespace std;

namespace zzc
{
	inline int read()
	{
		int x=0,f=1;char ch=getchar();
		while (!isdigit(ch)){if (ch==‘-‘) f=-1;ch=getchar();}
		while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
		return x*f;
	}
    
    const int maxn = 4e5+5;
    int n,qt;
    char ch[maxn];
    long long ans[maxn];

    struct suffix_automaton
    {
        int lst,cnt;
        int len[maxn],trans[maxn][26],link[maxn],pos[maxn];
        suffix_automaton(){cnt=lst=1;}
        int insert(int x)
        {
            int cur=++cnt,tmp=lst;lst=cnt;
            len[cur]=len[tmp]+1;
            for(;tmp&&!trans[tmp][x];tmp=link[tmp]) trans[tmp][x]=cur;
            if(!tmp)
            {
                link[cur]=1;
            }
            else
            {
                int q=trans[tmp][x];
                if(len[tmp]+1==len[q])
                {
                    link[cur]=q;
                }
                else
                {
                    int clone=++cnt;
                    len[clone]=len[tmp]+1;
                    link[clone]=link[q];
                    link[q]=link[cur]=clone;
                    for(int i=0;i<26;i++) trans[clone][i]=trans[q][i];
                    for(;tmp&&trans[tmp][x]==q;tmp=link[tmp]) trans[tmp][x]=clone;
                }
            }
            return cur;
        }
        void build()
        {
            for(int i=1;i<=n;i++) pos[i]=insert(ch[i]-‘a‘);
        }
    }sam;
    
    struct segment_tree
    {
        int ch[maxn][2],tag[maxn];
        long long sum[maxn];
        void pushup(int rt){sum[rt]=sum[lc]+sum[rc];}
        void add(int rt,int l,int r,int k)
        {
            tag[rt]+=k;
            sum[rt]+=1ll*k*(r-l+1);
        }
        void pushdown(int rt,int l,int r)
        {
            if(tag[rt])
            {
                int mid=(l+r)>>1;
                add(lc,l,mid,tag[rt]);
                add(rc,mid+1,r,tag[rt]);
                tag[rt]=0;
            }
        }
        void build(int rt,int l,int r)
        {
            sum[rt]=tag[rt]=0;
            if(l==r) return ;
            int mid=(l+r)>>1;
            build(lc,l,mid);build(rc,mid+1,r);
            pushup(rt);
        }
        void modify(int rt,int l,int r,int ql,int qr,int k)
        {
            if(ql<=l&&r<=qr)
            {
                add(rt,l,r,k);
                return ;
            }
            pushdown(rt,l,r);
            int mid=(l+r)>>1;
            if(ql<=mid) modify(lc,l,mid,ql,qr,k);
            if(qr>mid) modify(rc,mid+1,r,ql,qr,k);
            pushup(rt);
        }
        long long query(int rt,int l,int r,int ql,int qr)
        {
            if(ql<=l&&r<=qr) return sum[rt];
            pushdown(rt,l,r);
            int mid=(l+r)>>1;
            long long res=0;
            if(ql<=mid) res+=query(lc,l,mid,ql,qr);
            if(qr>mid) res+=query(rc,mid+1,r,ql,qr);
            return res;
        }
    }seg;

    struct LCT
    {
        int fa[maxn],ch[maxn][2],tag[maxn],val[maxn];
        void build()
        {
            for(int i=2;i<=sam.cnt;i++)
            {
                fa[i]=sam.link[i];
                ch[i][0]=ch[i][1]=0;
                val[i]=tag[i]=0;
            }
        }
        bool isroot(int x) {return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
        void assign(int x,int k) {val[x]=tag[x]=k;}
        void pushdown(int x) 
        {
            if(tag[x])
            {
                if(ch[x][0]) assign(ch[x][0],tag[x]);
                if(ch[x][1]) assign(ch[x][1],tag[x]);
                tag[x]=0;
            }
        }
        void pushall(int x)
        {
            if(!isroot(x)) pushall(fa[x]);
            pushdown(x);
        }
        void rotate(int x)
        {
            int y=fa[x],z=fa[y],l,r;
            if(ch[y][0]==x)l=0;else l=1;r=l^1;
            if(!isroot(y)){if(ch[z][0]==y)ch[z][0]=x;else ch[z][1]=x;}
            fa[x]=z;fa[y]=x;fa[ch[x][r]]=y;
            ch[y][l]=ch[x][r];ch[x][r]=y;
        }
        void splay(int x)
        {
            pushall(x);
            while(!isroot(x))
            {
                int y=fa[x],z=fa[y];
                if(!isroot(y)){if((ch[z][0]==y)^(ch[y][0]==x))rotate(x);else rotate(y);}
                rotate(x);
            }
        }
        void access(int x,int p)
        {
            int t=0;
            for(;x;t=x,x=fa[x])
            {
                splay(x);
                if(int k=val[x]) seg.modify(1,1,n,k-sam.len[x]+1,k-sam.len[fa[x]],-1);
                ch[x][1]=t;
            }
            assign(t,p);
            seg.modify(1,1,n,1,p,1);
        }
    }lct;

    struct que
    {
        int id,l,r;
        bool operator <(const que &b)const
        {
            return r==b.r?l<b.l:r<b.r;
        }
    }q[maxn];

	void work()
	{
	    scanf("%s",ch+1);n=strlen(ch+1);
        sam.build();
        lct.build();
        seg.build(1,1,n);
        qt=read();
        for(int i=1;i<=qt;i++)
        {
            q[i].l=read();q[i].r=read();
            q[i].id=i;
        }
        sort(q+1,q+qt+1);
        for(int i=1,j=1;i<=qt;i++)
        {
            while(j<=q[i].r) lct.access(sam.pos[j],j),j++;
            ans[q[i].id]=seg.query(1,1,n,q[i].l,q[i].r);
        }
        for(int i=1;i<=qt;i++) printf("%lld\n",ans[i]);
	}

}

int main()
{
	zzc::work();
	return 0;
}

P6292 区间本质不同子串个数 SAM+LCT+线段树

标签:space   ble   max   const   ++   auto   cond   维护   根据   

原文地址:https://www.cnblogs.com/youth518/p/14261529.html

版权声明:完美者 发表于 2021-01-13 10:49:22。
转载请注明:P6292 区间本质不同子串个数 SAM+LCT+线段树 | 完美导航

暂无评论

暂无评论...