原题链接:Tautonym Puzzle
前言
这道题是一道很有趣的构造题。我认为这道题的重点在于对题目要求的转化与转化过程中细节的处理。(有些细节问题也困惑了我很久)。
题意
构造一个字符串 \(S\) ,使 \(S\) 的所有子序列中,恰好有 \(N\) 个好串。
- 好串:一个字符串能分成两个相同的字符串。
思路
我们可以构造一个形如 \(X\) 的字符串,满足:
-
前一半为 \(1\) 到 \(num\) 的一种排列
-
后一半为 \(1\) 到 \(num\) 的升序或降序排列
显然,题目要求前一半的上升/下降子序列的数量为 \(N\)。
如何构造?我们拿后半段为升序排列来举例。
假设我们从小到大枚举字符,那么可以发现:
-
当前字符如果放最前面,那么总方案数会 \(+1\)。
-
放最后面,方案数 \(×2\) 或方案数 \(×2+1\)。
为什么有两种呢?这就是本题的重点了。
其实两种写法的区别在于对空串的计算。
第一种是先把空串加上,后面每次就不用计算空串的情况了。
第二种是先不加上空串,后面每次都考虑空串。
只要理解到这一点,本题思路就很清晰了。
总结
因为这道题可以升序或降序,先加空串或后加空串,所以共有四种写法。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 1e3 + 10;
int n, cnt1, cnt2, a[MAXN], b[MAXN], num = 101;
signed main()
{
cin >> n; n++;
while (n > 1) {
if (n & 1) a[++cnt1] = --num, n = n - 1;
else b[++cnt2] = --num, n = n / 2;
}
cout << (100 - num + 1) * 2 << endl;
for (int i = 1; i <= cnt1; i++) cout << a[i] << " ";
for (int i = cnt2; i >= 1; i--) cout << b[i] << " ";
for (int i = num ;i <= 100; i++) cout << i << " ";
return 0;
}
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN = 1e3 + 10;
int n, cnt1, cnt2, a[MAXN], b[MAXN], num = 101;
signed main()
{
cin >> n;
while (n >= 1) {
if (n & 1) a[++cnt1] = --num, n = (n - 1) / 2;
else b[++cnt2] = --num, n = n - 1;
}
cout << (100 - num + 1) * 2 << endl;
for (int i = 1; i <= cnt2; i++) cout << b[i] << " ";
for (int i = cnt1; i >= 1; i--) cout << a[i] << " ";
for (int i = num ;i <= 100; i++) cout << i << " ";
return 0;
}
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 1e3 + 10;
int n, cnt1, cnt2, a[MAXN], b[MAXN], num = 0;
signed main()
{
cin >> n; n++;
while (n > 1) {
if (n & 1) a[++cnt1] = ++num, n = n - 1;
else b[++cnt2] = ++num, n = n / 2;
}
cout << num * 2 << endl;
for (int i = 1; i <= cnt1; i++) cout << a[i] << " ";
for (int i = cnt2; i >= 1; i--) cout << b[i] << " ";
for (int i = num ;i >= 1; i--) cout << i << " ";
return 0;
}
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 1e3 + 10;
int n, cnt1, cnt2, a[MAXN], b[MAXN], num = 0;
signed main()
{
cin >> n;
while (n >= 1) {
if (n & 1) a[++cnt1] = ++num, n = (n - 1) / 2;
else b[++cnt2] = ++num, n = n - 1;
}
cout << num * 2 << endl;
for (int i = 1; i <= cnt2; i++) cout << b[i] << " ";
for (int i = cnt1; i >= 1; i--) cout << a[i] << " ";
for (int i = num ;i >= 1; i--) cout << i << " ";
return 0;
}