20200507省选模拟赛A(序列自动机+重链剖分+链上倍增)

 

 

 

题解

好题

其实序列自动机也不是什么高级的东西

但是重链剖分+链上倍增基本上就很难想得到了

还有巧妙的输出方案的方法:先输出后面再输出前面,如果够了就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");
	}
}

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值