Official Solution.
因为 个数最多有 种选取方法,而需要覆盖的数的数量也是 ,这就告诉我们构造的序列 需要满足所有的子集的异或均两两不同。
最小值
构造 即可。很显然不能得到和更小的序列。
最大值
构造序列 ,然后翻转整个序列即可。
下面证明一下这个为什么一定取到了最大值:首先如果存在一个 满足二进制第 位为 的话,那么就算其他数全取最大值是合法的,其和也无法比现在构造的序列的和要大。
而若存在两个二进制位,其在每个 内的值都相同,那么对于每个选出的 的子集得到的异或值,这两个二进制位都必然相同,也就必然无法表示出所有的数。因此任意两个二进制位,都至少存在一个位置 使得 在这两个二进制位上得到的值不同。因此其余二进制位必然不能全都是 。
而因为剩下 个二进制位和 个 的值没确定,所以考虑对剩下的每个二进制位都选择一个不同的 ,把其相应的二进制位值设为 。一个可行的方案就是 都有 ,然后将 序列从小到大排序即可。因为这样构造得到的 序列是单调递减的,所以可以直接翻转。
总时间复杂度为 。
:::success[代码实现]
namespace Loyalty
{
int a[N];
inline void init() {}
inline void main([[maybe_unused]] int _ca, [[maybe_unused]] int _atc)
{
int n;
cin >> n;
cout << 1 << '\n';
for (int i = 1; i <= n; ++i)
cout << (1ll << (i - 1)) << ' ';
cout << '\n';
a[1] = (1ll << n) - 1;
for (int i = 2; i <= n; ++i)
a[i] = (1ll << n) - 1 - (1ll << (i - 2));
for (int i = n; i; --i)
cout << a[i] << ' ';
cout << '\n';
}
} // namespace Loyalty:::
当然你也可以直接写个线性基上去直接算出来序列的下一个元素,当然这就没什么必要了()
喜欢的话,留下你的评论吧~