題解
好題
其實序列自動機也不是什麼高級的東西
但是重鏈剖分+鏈上倍增基本上就很難想得到了
還有巧妙的輸出方案的方法:先輸出後面再輸出前面,如果夠了就return
官方題解已經講得很清楚了
注意要先把所有的f初始化爲1,表示只選擇它自己的方案
代碼:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 300005
#define LOG 18
#define LL long long
const LL inf=0x3f3f3f3f3f3f3f3fll;
int n,m,ch[N][26],len,tp;
char S[N],a[N];
LL f[N],k;
struct node{
int p;LL s;
node(){}
node(int x,LL y){p=x;s=y;}
node operator + (const node &b)const{
return node(b.p,min(s+b.s,inf));
}
}nxt[N][LOG+2];
void print(int x,int i)
{
if(tp==len) return;
if(!i){S[++tp]=a[nxt[x][0].p];return;}
print(nxt[x][i-1].p,i-1);
print(x,i-1);
}
void solve(int x)
{
if(k==1) return;
bool vis[20]={0};
int pos[20],i,y;
for(i=LOG;i>=0;i--){
if(k>nxt[x][i].s&&k-nxt[x][i].s<=f[nxt[x][i].p]){
vis[i]=1;
pos[i]=x;
k-=nxt[x][i].s;
x=nxt[x][i].p;
}
}
if(k>1){
k--;
for(i=0;i<26;i++){
y=ch[x][i];
if(k>f[y])k-=f[y];
else{solve(y);if(tp<len)S[++tp]='a'+i;break;}
}
}
for(i=0;i<=LOG;i++)
if(vis[i])print(pos[i],i);
}
int main()
{
int i,j;
scanf("%s%d",a+1,&m);
n=strlen(a+1);
for(i=0;i<26;i++)ch[n][i]=n+1;
f[n]=1;
nxt[n][0]=node(n+1,1);
nxt[n+1][0]=node(n+1,0);
for(i=n-1;i>=0;i--){
memcpy(ch[i],ch[i+1],sizeof(ch[i+1]));
ch[i][a[i+1]-'a']=i+1;
f[i]=1;
for(j=0;j<26;j++){
f[i]+=f[ch[i][j]];
if(f[i]>=inf){f[i]=inf;break;}
}
int p=0;
for(j=0;j<26;j++)if(f[ch[i][j]]>f[ch[i][p]])p=j;
LL s=1;
for(j=0;j<p;j++){
s+=f[ch[i][j]];
if(s>=inf){s=inf;break;}
}
nxt[i][0]=node(ch[i][p],s);
}
for(j=1;j<=18;j++)
for(i=0;i<=n+1;i++)
nxt[i][j]=nxt[i][j-1]+nxt[nxt[i][j-1].p][j-1];
while(m--){
scanf("%lld%d",&k,&len);k++;
if(k>f[0]){puts("-1");continue;}
tp=0;solve(0);
for(i=tp;i>=1;i--)
printf("%c",S[i]);
printf("\n");
}
}