Legend
Link ( extrm{to Codeforces})。
给定 (n),求两个字符串 (s,t),使得 (t) 在 (s) 中作为子序列出现了恰好 (n) 次。
(1 le n le 10^6),(|s|,|t| le 200)。
Editorial
我想起有一道题,它是构造 (1447) 作为子序列的个数。也是 cf 的。
我就想啊,能不能用三个字符也做这道题呢?
那道 (1447) 的题限制字符串长度比较宽松,所以做法就是:
(14444444444cdots444)
然后把一堆的 (7) 贪心从右往左插入,如果左边有 (k) 个 (4),那么就贡献了 (inom{k}{2})。
受到这个做法的启发,我们来看这个题。
它没有限制组合数的下面一定是 (2),也就是 (inom{k}{2} o inom{k}{x}),而我们可以任意选取这个 (x)!
经过一番打表……(打表代码放在最后)
x = i
cnt 为估计最坏情况次数
mxneed 表示连续 '4' 段的长度最长是多少
i = 3 cnt = 366 mxneed = 182
i = 4 cnt = 145 mxneed = 71
i = 5 cnt = 91 mxneed = 43
i = 6 cnt = 70 mxneed = 32
i = 7 cnt = 63 mxneed = 27
i = 8 cnt = 58 mxneed = 24
i = 9 cnt = 59 mxneed = 23
i = 10 cnt = 59 mxneed = 22
i = 11 cnt = 62 mxneed = 22
i = 12 cnt = 62 mxneed = 22
i = 13 cnt = 65 mxneed = 22
i = 14 cnt = 67 mxneed = 23
我们可以发现大约 (x) 取 (x=8) 的时候是相当优秀的,可以通过这个题。
复杂度不会算,但是显然可以过。
Code
代码非常好写,只需要预处理组合数即可。
#include <bits/stdc++.h>
int C[1000][1000];
void init(){
for(int i = 0 ; i < 1000 ; ++i) C[i][0] = 1;
for(int i = 1 ; i < 1000 ; ++i){
for(int j = 1 ; j < 1000 ; ++j){
C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
}
}
int cnt = 0 ,mxneed;
for(int i = 3 ; i <= 100 ; ++i){
cnt = 0 ,mxneed = 0;
for(int j = 1 ; C[j][i] <= 1e6 ; ++j){
mxneed = j;
if(C[j - 1][i]) cnt += ((C[j][i] - 1 - C[j - 1][i]) / C[j - 1][i]) + 1;
}
// printf("i = %d cnt = %d mxneed = %d
" ,i ,cnt + 2 + mxneed ,mxneed);
}
}
int pos[1000];
int main(){
init();
putchar('a');
int n; std::cin >> n;
for(int i = 24 ; n ; --i){
int cnt = n / C[i][8];
pos[i] = cnt;
n -= cnt * C[i][8];
}
for(int i = 1 ; i <= 24 ; ++i){
putchar('b');
while(pos[i]--) putchar('c');
}
puts(" abbbbbbbbc");
}