詢問等價於在 \([l,r)\) 中找到最大的 \(i\) 知足 \(lcs(i,r)\geqslant i-l+1\)。把問題放到 \(Parent\) 樹上來考慮,設 \(len\) 爲 \(i,r\) 對應節點的 \(lca\) 的長度,條件變爲 \(i-len<l\)。node
考慮一種暴力,先線段樹合併維護出每一個節點的 \(\text{endpos}\) 集合,而後枚舉 \(r\) 對應節點的祖先,在祖先節點上線段樹二分來更新答案,這裏不會算進去 \(r\) 對應節點子樹內的點,由於這些點顯然不優。但對每一個祖先都作的話,複雜度是沒法接受的。c++
考慮對 \(Parent\) 樹進行重鏈剖分,只在到根的路徑上的每一個重鏈底部進行這個作法。重鏈上的其餘祖先節點的貢獻統一處理,將詢問離線掛到對應的若干重鏈上,重鏈上的點暴力將輕兒子子樹內的信息加到當前點的線段樹裏,由於輕兒子子樹大小和爲 \(O(n\log n)\),因此複雜度有保證。用線段樹合併來實現前綴信息合併,一樣是在線段樹上二分查詢。git
總複雜度爲 \(O(n \log^2 n)\)。spa
#include<bits/stdc++.h> #define maxn 400010 #define maxm 18000010 #define inf 1000000000 #define mid ((l+r)>>1) using namespace std; template<typename T> inline void read(T &x) { x=0;char c=getchar();bool flag=false; while(!isdigit(c)){if(c=='-')flag=true;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} if(flag)x=-x; } int n,q,las=1,tot=1; int ans[maxn],fa[maxn],len[maxn],ch[maxn][28],bel[maxn],siz[maxn],son[maxn],top[maxn]; char s[maxn]; struct edge { int to,nxt; edge(int a=0,int b=0) { to=a,nxt=b; } }e[maxn]; int head[maxn],edge_cnt; void add(int from,int to) { e[++edge_cnt]=edge(to,head[from]),head[from]=edge_cnt; } struct node { int l,r,id; node(int a=0,int b=0,int c=0) { l=a,r=b,id=c; } }; vector<node> ve[maxn]; struct ST { int tot; int rt[maxn],ls[maxm],rs[maxm],mn[maxm]; void modify(int l,int r,int pos,int v,int &cur) { if(!cur) mn[cur=++tot]=inf; mn[cur]=min(mn[cur],v); if(l==r) return; if(pos<=mid) modify(l,mid,pos,v,ls[cur]); else modify(mid+1,r,pos,v,rs[cur]); } int query(int L,int R,int l,int r,int v,int cur) { if(!cur||L>r||R<l||mn[cur]>=v) return 0; if(l==r) return l; int pos=0; if(pos=query(L,R,mid+1,r,v,rs[cur])) return pos; if(pos=query(L,R,l,mid,v,ls[cur])) return pos; return 0; } int merge(int x,int y) { if(!x||!y) return x+y; int p=++tot; mn[p]=min(mn[x],mn[y]); ls[p]=merge(ls[x],ls[y]),rs[p]=merge(rs[x],rs[y]); return p; } }T1,T2; void insert(int c,int id) { int p=las,np=las=++tot; len[np]=len[p]+1,bel[id]=np,T1.modify(1,n,id,id,T1.rt[np]),T2.modify(1,n,id,0,T2.rt[np]); while(p&&!ch[p][c]) ch[p][c]=np,p=fa[p]; if(!p) fa[np]=1; else { int q=ch[p][c]; if(len[q]==len[p]+1) fa[np]=q; else { int nq=++tot; memcpy(ch[nq],ch[q],sizeof(ch[q])); len[nq]=len[p]+1,fa[nq]=fa[q],fa[q]=fa[np]=nq; while(ch[p][c]==q) ch[p][c]=nq,p=fa[p]; } } } void dfs_son(int x) { siz[x]=1; for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; dfs_son(y),siz[x]+=siz[y],T1.rt[x]=T1.merge(T1.rt[x],T1.rt[y]); if(siz[y]>siz[son[x]]) son[x]=y; } } void dfs_chain(int x,int tp) { top[x]=tp; if(son[x]) dfs_chain(son[x],tp); for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(top[y]) continue; dfs_chain(y,y); } } void get(int l,int r,int x,int cur) { if(!cur) return; if(l==r) { T2.modify(1,n,l,l-len[x],T2.rt[x]); return; } get(l,mid,x,T1.ls[cur]),get(mid+1,r,x,T1.rs[cur]); } void dfs(int x) { for(int i=head[x];i;i=e[i].nxt) dfs(e[i].to); if(x!=top[x]) return; while(x) { for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=son[x]) get(1,n,x,T1.rt[e[i].to]); for(int i=0;i<ve[x].size();++i) { int l=ve[x][i].l,r=ve[x][i].r,id=ve[x][i].id; ans[id]=max(ans[id],T2.query(l,r-1,1,n,l,T2.rt[x])-l+1); } T2.rt[son[x]]=T2.merge(T2.rt[son[x]],T2.rt[x]),x=son[x]; } } int main() { scanf("%s",s+1),n=strlen(s+1),read(q); for(int i=1;i<=n;++i) insert(s[i]-'a',i); for(int i=2;i<=tot;++i) add(fa[i],i); dfs_son(1),dfs_chain(1,1); for(int i=1;i<=q;++i) { int l,r,x; read(l),read(r),x=bel[r]; while(x) { ve[x].push_back(node(l,r,i)); ans[i]=max(ans[i],T1.query(l,r-1,1,n,len[x]+l,T1.rt[x])-l+1),x=fa[top[x]]; } } dfs(1); for(int i=1;i<=q;++i) printf("%d\n",max(ans[i],0)); return 0; }