【题解】封印 [BJOI2020] [P6640] [Loj3298]

【题解】封印 [BJOI2020] [P6640] [Loj3298]

传送门:封印 \(\text{[BJOI2020] [P6640]}\) \(\text{[Loj3298]}\)

【分析】

\(\text{CF}\) 上有一堆这种类似的题。

按照套路,先求出 \(s\) 中以每个 \(i\) 作为右端点与 \(t\) 能匹配到的最长子串长度,记为 \(len(i)\)(用\(\text{SA}\)\(\text{SAM}\) 均可)。

那么对于一次询问,答案为 \(\max_{i=l}^{r}\{i-\max(l,i-len(i)+1)+1\}\),似乎不太好维护,但注意到 \(i-len(i)+1\) 是有单调性的(单调不减),也就是说我们可以把 \([l,r]\) 中的所有 \(i\) 分为两半,左边为 \(\max_{i=l}^{mid-1}\{i-l+1\}=mid-1-l+1\),右边为 \(\max_{i=mid}^{r}\{len(i)\}\),套个静态查询区间最大值的数据结构进行维护。求 \(mid\) 也很简单,把询问离线按 \(l\) 排序,一个指针扫过去就完了。如果强制在线就二分求 \(mid\)

写了个两只 \(\log\) 的树状数组跑得飞快(雾)。

【Code】

#include<algorithm>
#include<cstring>
#include<cstdio>
#define LL long long
#define Re register int
using namespace std;
const int N=2e5+3;
int n,T,len[N],Ans[N];char s[N],t[N];
struct QAQ{int l,r,id;inline bool operator<(const QAQ &O)const{return l<O.l;}}Q[N];
inline void in(Re &x){
    int f=0;x=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x=f?-x:x;
}
struct Suffix_Automaton{
    int O,last,link[N<<1],maxlen[N<<1],trans[N<<1][2];
    Suffix_Automaton(){last=O=1;}
    inline void insert(Re ch){
        Re z=++O,p=last;maxlen[z]=maxlen[p]+1;
        while(p&&!trans[p][ch])trans[p][ch]=z,p=link[p];
        if(!p)link[z]=1;
        else{
            Re x=trans[p][ch];
            if(maxlen[x]==maxlen[p]+1)link[z]=x;
            else{
                Re y=++O;maxlen[y]=maxlen[p]+1;
                for(Re i=0;i<2;++i)trans[y][i]=trans[x][i];
                while(p&&trans[p][ch]==x)trans[p][ch]=y,p=link[p];
                link[y]=link[x],link[x]=link[z]=y;
            }
        }
        last=z;
    }
    inline void build(){
        for(Re i=1;t[i];++i)insert(t[i]-'a');
        for(Re i=1,p=1,Len=0;i<=n;++i){
            Re a=s[i]-'a';
            while(p&&!trans[p][a])p=link[p],Len=maxlen[p];
            if(!p)p=1,Len=0;
            else p=trans[p][a],++Len;
            len[i]=min(Len,i);
        }
    }
}SAM;
struct BIT{
    int C[N];
    inline void add(Re x,Re v){while(x<=n)C[x]=max(C[x],v),x+=x&-x;}
    inline int ask(Re l,Re r){
        Re ans=0;
        while(l<=r){
            while(r-(r&-r)>=l)ans=max(ans,C[r]),r-=r&-r;
            ans=max(ans,len[r--]);
        }
        return ans;
    }
}TR;
int main(){
//    freopen("123.txt","r",stdin);
    scanf("%s%s",s+1,t+1),n=strlen(s+1);
    in(T),SAM.build();
    for(Re i=1;i<=n;++i)TR.add(i,len[i]);
    for(Re i=1;i<=T;++i)in(Q[i].l),in(Q[i].r),Q[i].id=i;
    sort(Q+1,Q+T+1);
    for(Re l=1,i=1,o=1;l<=n&&o<=T;++l){
        while(i<=n&&i-len[i]+1<l)++i;//找到第一个满足i-len[i]+1>=l的位置
        while(o<=T&&Q[o].l==l){
            if(i-1>=l)Ans[Q[o].id]=min(i-1,Q[o].r)-l+1;
            if(i<=Q[o].r)Ans[Q[o].id]=max(Ans[Q[o].id],TR.ask(max(i,l),Q[o].r));
            ++o;
        }
    }
    for(Re i=1;i<=T;++i)printf("%d\n",Ans[i]);
}
posted @ 2020-06-28 16:23  辰星凌  阅读(286)  评论(0编辑  收藏  举报