【题目链接】
【思路要点】
- 我们称前\(i\)个字符与后\(i\)个字符相同的单词为\(i\)界单词。
- 一个长度为\(N\)单词如果有界,那么其最小的界应当小于等于\(\frac{N}{2}\),因为否则,前\(i\)个字符与后\(i\)个字符就会存在重叠部分,我们一定可以构造出一个更小的界。
- 为了解决全部两问,我们要能够回答形如这样的问题:以字符串\(S\)开头的长度为\(N\)无界单词有多少个?
- 令\(dp_{i}\)表示以字符串\(S\)开头的长度为\(i\)无界单词的个数。
- 定义函数\(unbounded(i)\),返回\(S\)长度为\(i\)的前缀是否是无界单词,函数\(bound(x)\)返回\(S\)是否是\(x\)界单词。
- 那么当\(i≤|S|\),\(dp_{i}=unbounded(i)\)。
- 当\(i>|S|\),我们计算所有单词的个数减去有界单词的个数,考虑枚举有界单词的最小界\(j\),则有\(dp_i=2^{i-|S|}-\sum_{j=1}^{\frac{i}{2}}dp_j*c_{i,j}\)。
- 其中\(c_{i,j}=\begin{cases}2^{i-2*j}& \text{j≥|S|}\\2^{i-j-|S|}& \text{|S|>j&&|S|+j≤i}\\bound(|S|+j-i)& \text{|S|>j&&|S|+j>i}\end{cases}\)
- 时间复杂度\(O(TN^4)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 105; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, cnt, ans[MAXN]; unsigned long long dp[MAXN], bit[MAXN]; bool unbounded(int x) { for (int i = 1; i <= x - 1; i++) { bool valid = true; for (int j = 1; j <= i; j++) if (ans[j] != ans[x - i + j]) { valid = false; break; } if (valid) return false; } return true; } bool bound(int x) { for (int i = 1; i <= x; i++) if (ans[i] != ans[cnt - x + i]) return false; return true; } unsigned long long solve() { for (int i = 1; i <= n; i++) { if (i <= cnt) { dp[i] = unbounded(i); continue; } dp[i] = bit[i - cnt]; for (int j = 1; 2 * j <= i; j++) { if (cnt <= j) dp[i] -= dp[j] * bit[i - 2 * j]; else if (cnt + j <= i) dp[i] -= dp[j] * bit[i - j - cnt]; else { int tmp = cnt + j - i; dp[i] -= dp[j] * bound(tmp); } } } return dp[n]; } int main() { int T; read(T); while (T--) { unsigned long long k; read(n), read(k); for (int i = 0; i <= min(n, 63); i++) bit[i] = 1llu << i; cnt = 0; printf("%llu\n", solve()); for (int i = 1; i <= n; i++) { ans[++cnt] = 0; unsigned long long tmp = solve(); if (tmp < k) { k -= tmp; ans[cnt] = 1; } } for (int i = 1; i <= n; i++) printf("%c", ans[i] + 'a'); printf("\n"); } return 0; }