题目链接
题目解法
我们发现无界单词即为
b
o
r
d
e
r
border
border 长度为
0
0
0 的字符串,用
g
[
i
]
g[i]
g[i] 表示长度为
i
i
i 的无界单词的个数
考虑求解
g
[
i
]
g[i]
g[i],正难则反,用
2
i
−
b
o
r
d
e
r
2^i-border
2i−border长度不为
0
0
0 的字符串个数
这里给出一个结论:任何长度为
l
e
n
len
len 的字符串的最小
b
o
r
d
e
r
border
border 长度一定
≤
⌊
l
e
n
2
⌋
\leq \lfloor\frac{len}{2}\rfloor
≤⌊2len⌋,这个结论画个图,用反证法可以简单证明出来
我们可以枚举字符串的最小
b
o
r
d
e
r
border
border 长度
j
j
j,可以发现只有当字符串是无界单词时,才可能是最小的
b
o
r
d
e
r
border
border,所以
g
[
i
]
=
2
i
−
∑
j
=
1
⌊
i
2
⌋
g
[
j
]
∗
2
i
−
2
j
g[i]=2^i-\sum^{\lfloor\frac{i}{2}\rfloor}_{j=1}g[j]*2^{i-2j}
g[i]=2i−∑j=1⌊2i⌋g[j]∗2i−2j
考虑第二问
一位一位往后枚举,每一位试填
0
0
0,计算无界单词的个数,决定
0
,
1
0,1
0,1
继续考虑用
d
p
[
i
]
dp[i]
dp[i] 表示前
i
i
i 位为无界单词的字符串个数
分类讨论(若当前试填第
l
e
n
len
len 位):
- i < = l e n i<=len i<=len,即前 i i i 为确定,直接暴力判断即可
-
i
>
=
2
∗
l
e
n
i>=2*len
i>=2∗len
这种情况的做法和之前求 g g g 数组的方法一样
d p [ i ] = 2 i − l e n − ∑ j = 1 ⌊ i 2 ⌋ d p [ j ] ∗ 2 i − j − m a x ( l e n , j ) dp[i]=2^{i-len}-\sum^{\lfloor\frac{i}{2}\rfloor}_{j=1}dp[j]*2^{i-j-max(len,j)} dp[i]=2i−len−∑j=1⌊2i⌋dp[j]∗2i−j−max(len,j) -
l
e
n
<
i
<
2
∗
l
e
n
len<i<2*len
len<i<2∗len
可以分段考虑
当 1 ≤ j ≤ i − l e n 1\leq j\leq i-len 1≤j≤i−len 时,减去的为 ∑ d p [ j ] ∗ 2 i − l e n − j \sum dp[j]*2^{i-len-j} ∑dp[j]∗2i−len−j
当 i − l e n < j ≤ ⌊ i 2 ⌋ i-len<j\leq \lfloor\frac{i}{2}\rfloor i−len<j≤⌊2i⌋ 时,需要判断 [ 1 , l e n − i + j ] [1,len-i+j] [1,len−i+j] 与 [ i − j + 1 , l e n ] [i-j+1,len] [i−j+1,len] 是否相同,相同减去1
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int N(70);
int n,ans[N];
ull pw[N],g[N],dp[N];
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
bool same(int l1,int r1,int l2,int r2){
for(int i=0;i<=r1-l1;i++) if(ans[i+l1]!=ans[i+l2]) return 0;
return 1;
}
bool check(int rb){//判断1-rb的border是否为0
for(int i=1;i<=rb/2;i++) if(same(1,i,rb-i+1,rb)) return 0;
return 1;
}
void work(){
int n;ull k;
scanf("%d%llu",&n,&k);
printf("%llu\n",g[n]);
for(int len=1;len<=n;len++){
ans[len]=0;
for(int i=1;i<=n;i++){
if(i<=len) dp[i]=check(i);
else if(i>=2*len){
dp[i]=pw[i-len];
for(int j=1;j<=i/2;j++) dp[i]-=dp[j]*pw[i-max(j,len)-j];
}
else{
dp[i]=pw[i-len];
for(int j=1;j<=i-len;j++) dp[i]-=dp[j]*pw[i-len-j];
for(int j=i-len+1;j<=i/2;j++) if(same(1,len-i+j,i-j+1,len)) dp[i]-=dp[j];
}
}
if(dp[n]<k) k-=dp[n],ans[len]=1;
}
for(int i=1;i<=n;i++) putchar(ans[i]+'a');
puts("");
}
int main(){
pw[0]=1;
for(int i=1;i<=64;i++) pw[i]=pw[i-1]*2;
for(int i=1;i<=64;i++){
g[i]=pw[i];
for(int j=1;j<=i/2;j++) g[i]-=g[j]*pw[i-j*2];//最短border的长度
}
int T=read();
while(T--) work();
return 0;
}
//g[i]:长度为i的无界单词的数量