luogu4482 BJWC2018 Border 的四种求法

题意:Border就是一个串除了自己以外,最长的前缀使得它等于自己的一个后缀。现在给你一个串,每次问你 [ l , r ] [l,r] [l,r]的border长度。
数据范围: 1 ≤ ∣ S ∣ , q ≤ 2 × 1 0 5 1\leq |S|,q \leq 2\times 10^5 1S,q2×105(时限 5 s 5s 5s

做法:我们考虑这个border的前缀,如果答案后缀的开头在 i i i,则要满足 l c p ( l , i ) + i > r lcp(l,i)+i>r lcp(l,i)+i>r,然后我们发现这个式子很优美,我们把后缀树建出来,使用树剖维护,然后我们怎么办呢。我们发现这个 l c p ( i , l ) lcp(i,l) lcp(i,l)就是 i , l i,l i,l后缀节点的lca,这个在树剖上比较难维护。但是我们可以进行讨论,如果两个点在一条重链上,肯定是深度浅的点作为lca,所以就在线段树上维护两个东西,一个是 l c p lcp lcp l l l走上来的,另一个是 i i i走上来的。询问可以离线,从 l l l的位置一路往上更新,每次遇到重链,来一次单点修改,插入一个 r r r(或者 r − l c p r-lcp rlcp)。 i i i则从下到上,每遇到重链就在这个节点上面查 r − l c p r-lcp rlcp的最小值,下面查 r r r的最小值,只要遇到最小值就删掉,并更新答案,因为我们想要 i i i尽量小。
最后我们一波边扫 i i i,边插入询问。底层用堆什么的维护一下,就行了。
时间复杂度: O ( n l o g 2 ( n ) ) O(nlog^2(n)) O(nlog2(n))
代码:

#include<iostream>
#include<cstring>
#include<cassert>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<time.h>
#include<algorithm>
using namespace std;
#define REP(i,x,y) for(int i=x;i<=y;i++)
#define rep(i,n) REP(i,1,n)
#define rep0(i,n) REP(i,0,n-1)
#define repG(i,x) for(int i=pos[x];~i;i=e[i].next)
#define ll long long
#define db double
const int N=4e5+7;
const int INF=1e9+7;
struct sam{int a[26];int p,l;}t[N];
struct edge{int v,next;}e[N*2];
struct wt{int l,r,id;}Q[N];
char s[N];
int pos[N],w[N],as[N],id[N],dw[N];
int n,q,last=0,cnt=0,num=0,tot=0;
void add(int x,int y){e[num]=(edge){y,pos[x]}; pos[x]=num++;}
int inss(int x){
    t[++cnt].l=t[last].l+1;
    int i;
    for(i=last;~i;i=t[i].p){
        if(t[i].a[x])break;
        t[i].a[x]=cnt;
    }
    if(i<0){t[cnt].p=0; last=cnt; return last;}
    int p=t[i].a[x];
    if(t[p].l==t[i].l+1){t[cnt].p=p; last=cnt; return last;}
    int nm=cnt;
    t[++cnt]=t[p];
    t[nm].p=cnt;
    t[p].p=cnt;
    t[cnt].l=t[i].l+1;
    for(;~i;i=t[i].p){
        if(t[i].a[x]!=p)break;
        t[i].a[x]=cnt;
    }
    last=nm;
    return last;
}//建立sam
bool cmp(wt a,wt b){return a.l<b.l;}
int fa[N],top[N],sz[N],ind[N],dep[N],son[N];
void dfs1(int x,int f,int d){
    dep[x]=d;
    sz[x]=1;
    fa[x]=f;
    int maxn=0,id;
    repG(i,x){
        dfs1(e[i].v,x,d+1);
        if(sz[e[i].v]>maxn){
            maxn=sz[e[i].v];
            id=e[i].v;
        }
        sz[x]+=sz[e[i].v];
    }
    son[x]=id;
}
void dfs2(int x,int tp){
    top[x]=tp;
    ind[x]=++tot;
    id[tot]=x;
    if(sz[x]==1)return;
    dfs2(son[x],tp);
    repG(i,x)if(e[i].v!=fa[x]&&e[i].v!=son[x])dfs2(e[i].v,e[i].v);
}
struct pir{
    int v,x;
    friend bool operator <(pir x,pir y){if(x.v!=y.v)return x.v>y.v; return x.x<y.x;}
    friend bool operator ==(pir x,pir y){return (x.x==y.x)&(x.v==y.v);}
};
priority_queue<pir>q1[N],q2[N],sd1[N],sd2[N];//q1,q2分别表示两种情况,其余两个是用来删除
struct Tree{int l,r,m1,m2;}tree[N*4];
void build(int l,int r,int p){
    tree[p]=(Tree){l,r,INF,INF};
    if(l==r){
        q1[l].push((pir){INF,0});
        q2[l].push((pir){INF,0});
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,p*2);
    build(mid+1,r,p*2+1);
}
void update(int p){
    tree[p].m1=min(tree[p*2].m1,tree[p*2+1].m1);
    tree[p].m2=min(tree[p*2].m2,tree[p*2+1].m2);
}
void ins(int p,int x,int y,int d){
    int l=tree[p].l,r=tree[p].r;
    if(l==r){
        q1[l].push((pir){y,d});//未确定lca(lcp)
        q2[l].push((pir){y-t[id[x]].l,d});//确定了lca(lcp)
        tree[p].m1=q1[l].top().v;
        tree[p].m2=q2[l].top().v;
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid)ins(p*2,x,y,d);
    else ins(p*2+1,x,y,d);
    update(p);
}
void del(int p,int x,int y,int d){
    int l=tree[p].l,r=tree[p].r;
    if(l==r){//对应删除
        sd1[l].push((pir){y,d});
        sd2[l].push((pir){y-t[id[x]].l,d});
        while(sd1[l].top()==q1[l].top())sd1[l].pop(),q1[l].pop();
        while(sd2[l].top()==q2[l].top())sd2[l].pop(),q2[l].pop();
        tree[p].m1=q1[l].top().v;
        tree[p].m2=q2[l].top().v;
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid)del(p*2,x,y,d);
    else del(p*2+1,x,y,d);
    update(p);
}
int check1(int p,int L,int R,int d){//分别求两种情况最小值
    int l=tree[p].l,r=tree[p].r;
    if(L<=l&&R>=r){
        if(tree[p].m1>=d)return 0;
        if(l==r)return q1[l].top().x;
        if(tree[p*2].m1<d)return check1(p*2,L,R,d);
        return check1(p*2+1,L,R,d);
    }
    int mid=(l+r)>>1;
    if(L<=mid){
        if(R>mid){
            int q=check1(p*2,L,R,d);
            return q?q:check1(p*2+1,L,R,d);
        }
        return check1(p*2,L,R,d);
    }
    return check1(p*2+1,L,R,d);
}
int check2(int p,int L,int R,int d){
    int l=tree[p].l,r=tree[p].r;
    if(L<=l&&R>=r){
        if(tree[p].m2>=d)return 0;
        if(l==r)return q2[l].top().x;
        if(tree[p*2].m2<d)return check2(p*2,L,R,d);
        return check2(p*2+1,L,R,d);
    }
    int mid=(l+r)>>1;
    if(L<=mid){
        if(R>mid){
            int q=check2(p*2,L,R,d);
            return q?q:check2(p*2+1,L,R,d);
        }
        return check2(p*2,L,R,d);
    }
    return check2(p*2+1,L,R,d);
}
void wins(int x,int r,int d){while(x)ins(1,ind[x],r,d),x=fa[top[x]];}
void wdel(int x,int r,int d){while(x)del(1,ind[x],r,d),x=fa[top[x]];}
int wac(int x,int cc){
    while(x){
        int tp=top[x],ed=dw[top[x]];
        int gg=check1(1,ind[x],ind[ed],t[x].l+cc);
        if(gg)return gg;
        int hh=check2(1,ind[tp],ind[x],cc);
        if(hh)return hh;
        x=fa[top[x]];
    }
    return 0;
}
int main(){
    scanf("%s%d",s+1,&q);
    int n=strlen(s+1);
    n++;
    s[n]='a';
    t[0].p=-1;
    for(int i=n;i;i--)w[i]=inss(s[i]-'a')+1;
    memset(pos,-1,sizeof(pos));
    cnt++;
    for(int i=cnt;i;i--)t[i]=t[i-1];
    rep(i,cnt)t[i].p++;
    rep(i,cnt)add(t[i].p,i);
    rep(i,q)scanf("%d%d",&Q[i].l,&Q[i].r),Q[i].id=i;
    rep(i,q)as[i]=Q[i].r+1;
    sort(Q+1,Q+q+1,cmp);
    dfs1(1,0,1);
    dfs2(1,1);
    build(1,cnt,1);
    rep(i,cnt)dw[i]=i;
    rep(i,cnt)if(dep[dw[top[i]]]<dep[i])dw[top[i]]=i;
    int nw=1;
    rep(i,n){
        while(Q[nw].l<=i-1&&nw<=q)wins(w[Q[nw].l],Q[nw].r,nw),nw++;
        int oo=wac(w[i],i);
        while(oo)as[Q[oo].id]-=i,wdel(w[Q[oo].l],Q[oo].r,oo),oo=wac(w[i],i);
    }
    rep(i,q)printf("%d\n",as[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值