文章目录
R e s u l t Result Result
H y p e r l i n k Hyperlink Hyperlink
https://www.luogu.com.cn/problem/U142356?contestId=37784
D e s c r i p t i o n Description Description
给定一个长度为
n
n
n的串,
m
m
m个询问
每次询问一个
i
,
l
,
r
i,l,r
i,l,r表示
[
l
,
r
]
[l,r]
[l,r]开头的后缀与
i
i
i开头的后缀的
L
C
P
LCP
LCP,如果有多组解,输出字典序最小的那个
S o l u t i o n Solution Solution
首先建立出后缀数组然后 R M Q RMQ RMQ求 L C P LCP LCP
接着考虑一个区间的答案必然是这个区间内离
r
k
i
rk_i
rki最近的那两个中产生,也就是前驱和后继
区间前驱后继可以用主席树维护
如果后继更大,直接输出后继即可
如果前驱更大,由于我们要字典序最小,所以我们再二分一个值,直到后继大于前驱时,再输出后继
上述数据结构、二分之间都是分开的,所以时间复杂度为 O ( ( n + m ) log n ) O((n+m)\log n) O((n+m)logn)
C o d e Code Code
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define N 200010
using namespace std;int n,m,q,res,id;
char s[N];
int x[N],y[N],c[N],sa[N],rk[N];
inline void Qsort()
{
for(register int i=1;i<=m;i++) c[i]=0;
for(register int i=1;i<=n;i++) ++c[x[i]];
for(register int i=2;i<=m;i++) c[i]+=c[i-1];
for(register int i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
return;
}
inline void get_SA()
{
for(register int i=1;i<=n;i++) ++c[x[i]=s[i]];
for(register int i=2;i<=m;i++) c[i]+=c[i-1];
for(register int i=n;i>=1;i--) sa[c[x[i]]--]=i;
for(register int k=1;k<=n;k<<=1)
{
int num=0;
for(register int i=n-k+1;i<=n;i++) y[++num]=i;
for(register int i=1;i<=n;i++) if(sa[i]>k) y[++num]=sa[i]-k;
Qsort();swap(x,y);
x[sa[1]]=1;num=1;
for(register int i=2;i<=n;i++) x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
if(num==n) break;m=num;
}
return;
}
int h[N],height[N];
inline void get_height()
{
int k=0;
for(register int i=1;i<=n;i++) rk[sa[i]]=i;
for(register int i=1;i<=n;i++)
{
if(k) --k;
int j=sa[rk[i]-1];
while(s[i+k]==s[j+k]) k++;
height[rk[i]]=k;
}
return;
}
inline LL read()
{
char c;LL d=1,f=0;
while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
return d*f;
}
int f[N][21],log[N]={0,0,1};
inline int LCP(int l,int r)
{
if(l+r==0) return -1;
if(l==r) return n-sa[l]+1;
if(l>r) swap(l,r);
l++;int z=log[r-l+1];
return min(f[l][z],f[r-(1<<z)+1][z]);
}
int cnt,rt[N],L[N<<5],R[N<<5],sum[N<<5];
inline int build(int l,int r)
{
int rt=++cnt,mid=l+r>>1;
if(l==r) {L[rt]=R[rt]=rt;return rt;}
L[rt]=build(l,mid);R[rt]=build(mid+1,r);
return rt;
}
inline int updata(int pre,int l,int r,int x)
{
int rt=++cnt,mid=l+r>>1;
L[rt]=L[pre];R[rt]=R[pre];sum[rt]=sum[pre]+1;
if(l<r)
{
if(x<=mid) L[rt]=updata(L[pre],l,mid,x);
else R[rt]=updata(R[pre],mid+1,r,x);
}
return rt;
}
inline int query(int p,int q,int l,int r,int k)
{
if(l>=r) return l;
int mid=l+r>>1,s=sum[L[q]]-sum[L[p]];
if(s>=k) return query(L[p],L[q],l,mid,k);
else return query(R[p],R[q],mid+1,r,k-s);
}
inline int Ask(int p,int q,int l,int r,int ql,int qr)
{
if(ql>qr||(sum[q]-sum[p])==0) return 0;
if(ql<=l&&r<=qr) return sum[q]-sum[p];
int mid=l+r>>1,res=0;
if(ql<=mid) res+=Ask(L[p],L[q],l,mid,ql,qr);
if(qr>mid) res+=Ask(R[p],R[q],mid+1,r,ql,qr);
return res;
}
signed main()
{
scanf("%s",s+1);n=strlen(s+1);m=127;
get_SA();get_height();
for(register int i=1;i<=n;i++) f[i][0]=height[i];
for(register int i=3;i<=n;i++) log[i]=log[i>>1]+1;
for(register int j=1;1<<j<=n;j++) for(register int i=1;i+(1<<j)-1<=n;i++)
f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]);
q=read();
rt[0]=build(1,n);
for(register int i=1;i<=n;i++) rt[i]=updata(rt[i-1],1,n,rk[i]);
while(q--)
{
int x=read(),l=read(),r=read(),lower=0,upper=0;
x=rk[x];
int Ans=0,Sum=Ask(rt[l-1],rt[r],1,n,1,x-1);
if(Sum<sum[rt[r]]-sum[rt[l-1]]) upper=query(rt[l-1],rt[r],1,n,Sum+1);
if(Sum>0) lower=query(rt[l-1],rt[r],1,n,Sum);
Ans=LCP(x,upper);Sum=LCP(lower,x);
if(Ans>Sum)
{
printf("%d %d\n",Ans,sa[upper]);
continue;
}
int L=1,R=x;
while(L<=R)
{
int mid=(L+R)>>1;
if(LCP(mid,x)<Sum) L=mid+1;else R=mid-1;
}
upper=Ask(rt[l-1],rt[r],1,n,1,L-1);
printf("%d %d\n",Sum,sa[query(rt[l-1],rt[r],1,n,upper+1)]);
}
}