题意:给一个字符串,求区间
题解:
对于一个串
,求的就是
这启示我们从
开始跳
树,到一个点的
的
就是当前的
然后用线段树合并维护
集合
在
集合中找一个满足
的最大的
显然一步一步跳不行,考虑树剖,一次跳一个链,对答案有贡献的是链的一个前缀
离线,链分治,将一个询问拆成
个,再对每一条重链单独处理
由于有贡献的是一个前缀,我们动态从上向下做
,从上到下做的时候暴力将轻儿子的所有点插入某一种数据结构
来看看这种数据结构需要干什么
就是在一个区间
找一个满足
的最大的
线段树上二分,维护
的最小值
这样一来就解决了重链上一个前缀的所有轻儿子的贡献
还差一个链上的点的重儿子的贡献
对于这个重儿子的贡献,我们用最开始的线段树合并的方法就可以解决
复杂度
一句话题解:
将问题转换为跳
树,进一步转换为跳重链,链分治对每一条重链分别处理,处理的时候
暴力处理轻儿子,线段树合并处理重儿子
感觉链分治的思想蛮巧妙的!
#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch=getchar(); if(ch=='-')f=-1; }
while(isdigit(ch)) cnt=cnt*10+(ch-'0'),ch=getchar();
return cnt * f;
}
cs int N = 4e5 + 5;
char s[N];
int n,m,ql[N],qr[N],ans[N];
int ps[N],bin[N];
namespace SAM{
int ch[N][26],lk[N],len[N],nd;
void init(){ nd=n+1; for(int i=1; i<=n; i++) len[i]=i; }
void extend(int i, int c){
int now=i,p=(i-1)?(i-1):n+1;
for(;p&&!ch[p][c];p=lk[p]) ch[p][c]=now;
if(!p) lk[now]=n+1;
else{
int q=ch[p][c];
if(len[q]==len[p]+1) lk[now]=q;
else{
int cl=++nd; len[cl]=len[p]+1; lk[cl]=lk[q];
memcpy(ch[cl],ch[q],sizeof(ch[q]));
lk[now]=lk[q]=cl;
for(;p&&ch[p][c]==q;p=lk[p]) ch[p][c]=cl;
}
}
}
void radix_sort(){
for(int i=1; i<=nd; i++) ++bin[len[i]];
for(int i=1; i<=n; i++) bin[i]+=bin[i-1];
for(int i=nd; i>=1; i--) ps[bin[len[i]]--]=i;
}
}
int son[N],top[N],sz[N],fa[N];
vector<int>G[N];
void Div(){
for(int i=SAM::nd; i>=1; i--){
int u=ps[i]; fa[u]=SAM::lk[u];
sz[fa[u]]+=++sz[u];
if(sz[son[fa[u]]]<sz[u]) son[fa[u]]=u;
G[fa[u]].push_back(u);
}
for(int i=1; i<=SAM::nd; i++){
int u=ps[i]; if(!top[u]) top[u]=u;
if(son[u]) top[son[u]]=top[u];
}
}
vector<int>v[N];
void addqry(int i,int u){
if(ql[i]==qr[i]) return;
while(u) v[u].push_back(i),u=fa[top[u]];
}
namespace SGT1{
cs int M=N*40;
int rt[N],mx[M],ls[M],rs[M],nd;
#define mid ((l+r)>>1)
void ins(int &x, int l, int r, int p){
if(!x) x=++nd; mx[x]=max(mx[x],p); if(l==r) return;
(p<=mid)?ins(ls[x],l,mid,p):ins(rs[x],mid+1,r,p);
}
int merge(int x, int y){
if(!x||!y) return x|y;
ls[x]=merge(ls[x],ls[y]);
rs[x]=merge(rs[x],rs[y]);
mx[x]=max(mx[ls[x]],mx[rs[x]]); return x;
}
int query(int x, int l, int r, int L, int R){
if(!x) return 0; if(L<=l&&r<=R) return mx[x]; int ans=0;
if(L<=mid) ans=max(ans,query(ls[x],l,mid,L,R));
if(R>mid) ans=max(ans,query(rs[x],mid+1,r,L,R)); return ans;
}
void Main(){
for(int i=1; i<=n; i++) ins(rt[i],1,n,i);
for(int i=SAM::nd;i>=1;i--){
int u=ps[i];
for(int id:v[u])
ans[id]=max(ans[id],query(rt[u],1,n,ql[id],min(ql[id]+SAM::len[u]-1,qr[id]-1))-ql[id]+1);
rt[fa[u]]=merge(rt[fa[u]],rt[u]);
}
}
#undef mid
}
namespace SGT2{
cs int N=::N<<2;
int ls[N],rs[N],mi[N],rt,nd;
#define mid ((l+r)>>1)
void ins(int &x, int l, int r, int p, int v){
if(!x){ x=++nd; ls[x]=rs[x]=0; mi[x]=1e9; }
mi[x]=min(mi[x],v); if(l==r) return;
(p<=mid)?ins(ls[x],l,mid,p,v):ins(rs[x],mid+1,r,p,v);
}
int query(int x, int l, int r, int L, int R, int v){
if(!x||mi[x]>v) return 0; if(l==r) return l;
if(R<=mid) return query(ls[x],l,mid,L,R,v);
if(L>mid) return query(rs[x],mid+1,r,L,R,v);
int ans=0;
if(rs[x]&&mi[rs[x]]<=v) ans=query(rs[x],mid+1,r,L,R,v);
if(ans) return ans;
return query(ls[x],l,mid,L,R,v);
}
void dsu(int u, int len){
if(u<=n) ins(rt,1,n,u,u-len+1);
for(int t:G[u]) dsu(t,len);
}
void dfs(int u){
for(int p=u;p;p=son[p])
for(int t:G[p]) if(t^son[p]) dfs(t);
rt=nd=0;
for(int p=u;p;p=son[p]){
for(int t:G[p]) if(t^son[p]) dsu(t,SAM::len[p]);
if(p<=n) ins(rt,1,n,p,p-SAM::len[p]+1);
for(int id:v[p])
ans[id]=max(ans[id],query(rt,1,n,ql[id],qr[id]-1,ql[id])-ql[id]+1);
}
}
}
int main(){
scanf("%s",s+1); n=strlen(s+1); SAM::init();
for(int i=1; i<=n; i++) SAM::extend(i,s[i]-'a');
SAM::radix_sort(); Div();
m=read();
for(int i=1; i<=m; i++){
ql[i]=read(),qr[i]=read();
addqry(i,qr[i]);
}
SGT1::Main();
SGT2::dfs(n+1);
for(int i=1; i<=m; i++) cout<<ans[i]<<'\n';
return 0;
}