zoukankan      html  css  js  c++  java
  • 「NOI2018」 你的名字

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

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

    Description

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

    (|S|le 5 imes 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) 集合

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

    然后考虑对于 ([ldots 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
    ",ans); 
            }
            return 0;
        }
    }
    signed main(){return yspm::main();}
    
  • 相关阅读:
    对 Spring IoC 的理解
    初识 Spring 框架
    CSS 全局样式
    Bootstrap 12 栅格系统
    551 闭包,浏览器垃圾回收机制/内存收机制
    550 JavaScript运行机制之“堆栈”
    549 数据类型转换汇总:转换为Number、字符串、布尔,比 较操作==、===,练习题
    547 Promise:Ajax 的串行、并行, Promise的executor和状态,then、catch、finally,then链
    546 变量提升
    545 parseInt解析
  • 原文地址:https://www.cnblogs.com/yspm/p/14186025.html
Copyright © 2011-2022 走看看