ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-01-11 12:36:50  阅读:274  来源: 互联网

标签:rt 子串 LCT SAM int mid 区间 P6292 ql


题意:

戳这里

分析:

  • 前置芝士: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;
}

标签:rt,子串,LCT,SAM,int,mid,区间,P6292,ql
来源: https://www.cnblogs.com/youth518/p/14261529.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有