题意
给一个长度为n的字符串,把这个串的所有子串按字典序排序后拼到一起组成一个新串,每次询问新串的某个字符是什么。
n
≤
200000
n\le200000
n≤200000,强制在线。
分析
先通过建反串的后缀自动机把原串的后缀树建出来,不难发现把子串按字典序排序等价于求出后缀树的dfs序。
那么我们只要在dfs序上二分出对应节点,然后在对应节点里面算一算就好了。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
typedef long long LL;
const int N=200005;
const int M=400005;
int n,sz,tot,a[M],b[M],c[M],rig[M],ch[M][26],pos[M],mx[M],last,fa[M],son[M][26];
char str[N];
LL s[M];
void extend(int x,int i)
{
int p,q,np,nq;
p=last;last=np=++sz;mx[np]=mx[p]+1;rig[np]++;pos[np]=i;
for (;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
if (!p) fa[np]=1;
else
{
q=ch[p][x];
if (mx[q]==mx[p]+1) fa[np]=q;
else
{
nq=++sz;mx[nq]=mx[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for (;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
}
}
}
void pre()
{
for (int i=1;i<=sz;i++) b[mx[i]]++;
for (int i=1;i<=sz;i++) b[i]+=b[i-1];
for (int i=sz;i>=1;i--) c[b[mx[i]]--]=i;
for (int i=sz;i>=1;i--)
{
int x=c[i],y=fa[c[i]];
rig[y]+=rig[x];pos[y]=pos[x];
son[y][str[pos[x]-mx[y]]-'a']=x;
}
}
void dfs(int x)
{
a[++tot]=x;
for (int i=0;i<26;i++)
if (son[x][i]) dfs(son[x][i]);
}
LL get_sum(int l,int r)
{
return (LL)(l+r)*(r-l+1)/2;
}
int solve(LL k)
{
int l=1,r=sz;
while (l<=r)
{
int mid=(l+r)/2;
if (s[mid]<k) l=mid+1;
else r=mid-1;
}
k-=s[l-1];int t=a[l],h=mx[fa[t]]+1;l=h;r=mx[t];
while (l<=r)
{
int mid=(l+r)/2;
if ((LL)rig[t]*get_sum(h,mid)<k) l=mid+1;
else r=mid-1;
}
k-=(LL)rig[t]*get_sum(h,l-1);
k=(k-1)%l+1;
return str[pos[t]-k+1];
}
int main()
{
scanf("%s",str+1);
n=strlen(str+1);
std::reverse(str+1,str+n+1);
last=sz=1;
for (int i=1;i<=n;i++) extend(str[i]-'a',i);
pre();dfs(1);
for (int i=1;i<=tot;i++) s[i]=s[i-1]+(LL)get_sum(mx[fa[a[i]]]+1,mx[a[i]])*rig[a[i]];
int q,G=0;scanf("%d",&q);
while (q--)
{
int p,ans;LL m;scanf("%d%lld",&p,&m);
LL k=(LL)p*G%m+1;
G+=(ans=solve(k));
putchar(ans);puts("");
}
return 0;
}