题目大意
给定一个N和
有界单词:对于一个单词
无界单词:对于一个单词S,如果不存在一个
题目有T组测试数据。
解题思路
相比第二问来说,肯定是第一问比较好处理,那我们先来考虑一下第一问。
我们可以令Fi表示长度为i的单词有多少个是无序的,计算时无界单词考虑起来比较繁琐,所以可以用所有单词的数量减去有界单词的数量来得到
假如当前的有界单词长度为
对于第二问,容易想到的是我们可以一位一位的判断能不能放a,现在的问题就变成了如何统计一个确定前
假设当前已经确定了前
i≤Len
那么当前要求的前Len个字符都是已经确定。所以直接用KMP判断一下已经确定的前i个字符组成的单词是否是无序单词就可以了。Len≤j
那么对当前的Fi造成影响的Fj都在前面的转移中特殊考虑过了,所以第一问一样直接转移就可以了。Len>j,Len≤i−j
这种情况跟第二种差不多,但是相同的前后缀中有些任选的位置已经在被确定了,所以只需把中间任选的方案(2i−2j),改成除了确定的位置任选(2i−2j−(Len−j))Len>i−j
当出现这种情况时不仅中间任选字符已经确定,而且我们把前缀复制到后缀时,放后缀的有些位置的字符也已经确定。所以我们只需用Hash判断一下当前超出的位置是不是当前单词的前缀。而由于可以任选字符的位置已经确定,所以如果匹配成功,只需把有序单词的个数加+E。
程序
//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef unsigned long long LL;
const int MAXN = 65, HNum = 15731;
LL K, Pow[MAXN], F[MAXN], Has[MAXN], H[MAXN];
int Len, Last, Next[MAXN], N, Pj, Ans[MAXN];
void Prepare() {
Pow[0] = Has[0] = 1;
for (int i = 1; i <= 64; i ++) Pow[i] = Pow[i - 1] * 2;
for (int i = 1; i <= 64; i ++) Has[i] = Has[i - 1] * HNum;
}
bool Judge(int Len, int r) {
int l = r - Len + 1;
return H[Len] - H[0] * Has[Len] == H[r] - H[l - 1] * Has[r - l + 1];
}
LL Solve() {
memset(F, 0, sizeof F);
for (int i = 1; i <= N; i ++) {
if (i < Len && !Next[i]) F[i] = 1;
if (i < Len) continue;
F[i] = Pow[i - Len];
for (int j = 1; j <= i / 2; j ++) {
LL Cnt;
if (Len <= j) Cnt = Pow[i - 2 * j];
if (Len > j && Len <= i - j) Cnt = Pow[i - 2 * j - (Len - j)];
if (Len > i - j) Cnt = Judge(Len - (i - j), Len);
F[i] -= F[j] * Cnt;
}
}
return F[N];
}
void PushIn(int c) {
Ans[++ Len] = c, H[Len] = H[Len - 1] * HNum + c;
if (Len == 1) return;
for (; Pj && Ans[Pj + 1] != Ans[Len]; Pj = Next[Pj]);
if (Ans[Pj + 1] == Ans[Len]) Pj ++;
Next[Len] = Pj;
}
void Work() {
Len = 0, Pj = 0;
scanf("%d%lld", &N, &K);
printf("%lld\n", Solve());
for (int i = 1; i <= N; i ++) {
Last = Pj;
PushIn(0);
LL Num = Solve();
if (Num < K) {
Pj = Last, Len --;
K -= Num;
PushIn(1);
}
}
for (int i = 1; i <= N; i ++) printf("%c", Ans[i] + 'a');
printf("\n");
}
int main() {
Prepare();
int Test;
scanf("%d", &Test);
for (; Test; Test --) Work();
}