其他分享
首页 > 其他分享> > 「NOI2018」 你的名字

「NOI2018」 你的名字

作者:互联网

怕是以后在也不能做非思维题了,要不然都想不出来啥了

其实早知道是线段树合并,然后拍上一个维护 \(endpos\) 就开始坐以待毙了

Description

给定 \(S\) 和若干个询问,每个询问是 \(T,l,r\) 的形式,求 \(T\) 有多少个子串在 \(S[l\dots r]\) 中没有出现过

\(|S|\le 5\times 10^5,\sum |T|\le 10^6\)

其中对于 \(68pts\) 的部分分满足 \(l=1,r=|S|\)

Solution

首先考虑一个 \(n^2\) 的做法是对于每个 \(T\) 建 \(SAM\) 然后跑可达性统计,然后两个串一起跑,如果这个点在 \(T\) 里面能跑到 \(S\) 里面没有就加上可达的点

貌似也没啥优化的空间,那么换一个思路,往 后缀树 上面靠

直接统计还是不太行,那么正难则反,用总的 \(T\) 上的减掉公共子串

具体就是求一下 \(l[i]\) 表示 \(T\) 到 \(i\) 的前缀有多长可以在 \(S\) 上面作为一段后缀出现

如果如果不考虑存在重复的情况,就把 \(T\) 放到 \(S\) 上面跑,每次不行了就跳 \(fa\)

(这里深刻一个观点:后缀树上面点的深度基本上是没啥用的,含义就可以轻松得证)

因为 \(fa\) 里面的都是 \(x\) 的后缀,又要选最大值,所以每跳一下,长度减成现在点的 \(len\)

那么这就得到了一个 \(68pts\) 的做法


接着考虑怎么做不是全串的情况,先求所有的 \(endpos\) 集合

(如果对后缀树的相关熟练的话,其实这步是很好说的)

然后考虑对于 \([l\dots r]\) 的限制怎么写

然而并不能每次暴力重建,但是每次只是找儿子,跳父亲,找当前点在 \([l,r]\) 内的最大 \(len\)

第一个其实对应的是 \(endpos\) 有没有在 \([l,r]\) 之间有点

第二个是跳父亲,因为父亲的 \(endpos\) 是母集,随便跳

最后一个比较恶心,想清楚了是 \(min(len[i],now-max(now,pos)+1)\)

\(pos\) 是指在 \([l,r]\) 内的最大的 \(endpos\) 下标,这个可以用线段树求出来

还是一样的跑就行了

本文中加粗的部分是瓶颈,观察后缀自动机在什么时候用和跑 \(pos\) 这两个套路确实厉害

仿佛 \(CF\) 上有类似套路题?

Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define reg register
namespace yspm{
    inline int read(){
        int res=0,f=1; char k;
        while(!isdigit(k=getchar())) if(k=='-') f=-1;
        while(isdigit(k)) res=res*10+k-'0',k=getchar();
        return res*f;
    }
    const int N=5e5+10,M=1e6+10,SZ=M*40;
    int ls[SZ],rs[SZ],sum[SZ],tot,rt[M];
    inline int max(int x,int y){return x>y?x:y;}
    struct Saffix_Automation{
        int son[M][26],len[M],fa[M],tot,las,id[M];
        inline void extend(int x,int pos,bool fl){
            int tmp=las,np=las=++tot; len[np]=len[tmp]+1; id[np]=pos;
            while(!son[tmp][x]) son[tmp][x]=np,tmp=fa[tmp];
            if(!tmp) return fa[np]=1,void();
            int q=son[tmp][x]; if(len[q]==len[tmp]+1) return fa[np]=q,void();
            int clone=++tot; len[clone]=len[tmp]+1; fa[clone]=fa[q]; fa[q]=fa[np]=clone; 
            for(reg int i=0;i<26;++i) son[clone][i]=son[q][i]; if(fl) id[clone]=id[np];
            while(son[tmp][x]==q) son[tmp][x]=clone,tmp=fa[tmp];  
            return ;    
        }
        inline void clear(){
            for(reg int i=1;i<=tot;++i) fa[i]=len[i]=id[i]=0,memset(son[i],0,sizeof(son[i])); las=tot=1; 
            return ;
        }
        inline void init(){tot=1; las=1; return;}
        inline int num(int x){return len[x]-len[fa[x]];}
    }S1,S2;
    
    inline void push_up(int x){return sum[x]=sum[ls[x]]+sum[rs[x]],void();}
    inline void upd(int &p,int l,int r,int pos){
        if(!p) p=++tot; 
        if(l==r) return ++sum[p],void();
        int mid=(l+r)>>1; if(pos<=mid) upd(ls[p],l,mid,pos); else upd(rs[p],mid+1,r,pos);
        return push_up(p);
    }
    inline int merge(int x,int y,int l,int r){
        if(!x||!y) return x+y;
        if(l==r) return sum[x]|=sum[y],x;
        int mid=(l+r)>>1,p=++tot;
        ls[p]=merge(ls[x],ls[y],l,mid); rs[p]=merge(rs[x],rs[y],mid+1,r);
        return push_up(p),p;
    }
    ll ans;
    int head[M],cnt,mx[M],now,nowlen,len,slen; char s[M];
    struct edge{int to,nxt;}e[M];
    inline void add(int u,int v){
        e[++cnt].to=v; e[cnt].nxt=head[u];
        return head[u]=cnt,void();
    }
    inline void dfs(int x){
        if(S1.id[x]) upd(rt[x],1,slen,S1.id[x]);
        for(reg int i=head[x];i;i=e[i].nxt) dfs(e[i].to),rt[x]=merge(rt[x],rt[e[i].to],1,slen);
        return ;
    }
    inline int query(int p,int l,int r,int st,int ed){
        if(!p) return 0;
        if(st<=l&&r<=ed) return sum[p];
        int mid=(l+r)>>1; 
        if(st<=mid&&ls[p]&&query(ls[p],l,mid,st,ed)) return 1;
        if(ed>mid&&rs[p]&&query(rs[p],mid+1,r,st,ed)) return 1;  
        return 0;
    }
    signed main(){
        scanf("%s",s+1); slen=strlen(s+1); S1.init();
        for(reg int i=1;i<=slen;++i) S1.extend(s[i]-'a',i,0);
        for(reg int i=1;i<=S1.tot;++i) add(S1.fa[i],i); dfs(1);
        int T=read(),l,r; while(T--){
            S2.clear(); scanf("%s",s+1); len=strlen(s+1); l=read(); r=read();  
            for(reg int i=1;i<=len;++i) S2.extend(s[i]-'a',i,1);
            for(reg int i=1;i<=len;++i) mx[i]=0; ans=0; now=1; nowlen=0;
            for(reg int i=1;i<=len;++i){
                while(1){
                    int nxt=S1.son[now][s[i]-'a']; 
                    if(nxt&&query(rt[nxt],1,slen,l+nowlen,r)){nowlen++,now=nxt; break;}
                    if(!nowlen) break; --nowlen;
                    if(nowlen==S1.len[S1.fa[now]]) now=S1.fa[now];
                } mx[i]=nowlen; 
            }
            for(reg int i=2;i<=S2.tot;++i) ans+=max(0,S2.len[i]-max(S2.len[S2.fa[i]],mx[S2.id[i]]));
            printf("%lld\n",ans); 
        }
        return 0;
    }
}
signed main(){return yspm::main();}

标签:tmp,return,int,len,fa,名字,np,NOI2018
来源: https://www.cnblogs.com/yspm/p/14186025.html