Codeforces第 941 轮[B]缺失子序列和题解

题目详情

B. Missing Subsequence Sum

You are given two integers 𝑛 and 𝑘. Find a sequence 𝑎𝑎 of non-negative integers of size at most 25 such that the following conditions hold.

  • There is no subsequence of 𝑎𝑎 with a sum of 𝑘.
  • For all 1≤𝑣≤𝑛 where 𝑣≠𝑘, there is a subsequence of 𝑎 with a sum of 𝑣.

A sequence 𝑏 is a subsequence of 𝑎 if 𝑏 can be obtained from 𝑎 by the deletion of several (possibly, zero or all) elements, without changing the order of the remaining elements. For example, [5,2,3]is a subsequence of [1,5,7,8,2,4,3].

It can be shown that under the given constraints, a solution always exists.

Input

The first line of the input contains a single integer 𝑡 (1≤𝑡≤1000) — the number of test cases. The description of the test cases follows.

Each test case consists of a single line containing two integers 𝑛𝑛 and 𝑘𝑘 (2≤𝑛≤106, 1≤𝑘≤𝑛) — the parameters described above.

It is guaranteed that the sum of 𝑛𝑛 over all test cases does not exceed 107.

Output

The first line of output for each test case should contain a single integer 𝑚 (1≤𝑚≤25) — the size of your chosen sequence.

The second line of output for each test case should contain 𝑚𝑚 integers 𝑎𝑖 (0≤𝑎𝑖≤109) — the elements of your chosen sequence.

If there are multiple solutions, print any.

中文的大致题意翻译:

题目要求给定两个整数 𝑛 和 𝑘,找到一个非负整数序列 𝑎,其长度最多为 25,满足以下条件:

1. 序列𝑎中不存在子序列的和为 𝑘。
2. 对于所有 1 ≤ 𝑣 ≤ 𝑛 且 𝑣 ≠ 𝑘,序列𝑎中存在一个子序列的和为 𝑣。

要求编写一个程序,接受输入包含多个测试用例,每个测试用例包含两个整数 𝑛 和 𝑘。第一行输入一个整数 𝑡 表示测试用例数量(1 ≤ 𝑡 ≤ 1000)。每个测试用例描述如下:

- 第一行包含两个整数 𝑛 和 𝑘(2 ≤ 𝑛 ≤ 10^6,1 ≤ 𝑘 ≤ 𝑛)。

保证所有测试用例中 𝑛 的总和不超过 10^7。

对于每个测试用例,输出两行:

- 第一行输出一个整数 𝑚(1 ≤ 𝑚 ≤ 25),表示所选序列的长度。
- 第二行输出 𝑚 个整数,表示所选序列的元素。若存在多个解,则输出任意一个即可。、

解题思路

对于一个固定的k值,我们可以构造一个数列,使得对于任意小于k的数值,都可以找到一个子序列的和等于它。这意味着,只要我们能够找到一个数列满足条件,我们就可以忽略输入中的 𝑛,并假设它始终为 10^{6}

那么我们应该怎么构造这个序列呢,如果没有限制条件,最自然的做法是从1开始,每次将前面一个数乘以2,直到达到一个足够大的值,这样,任何数都可以用这个数列的某个子序列的和表示出来。

但是,题目要求我们不能有子序列的和等于k,所以我们仍需要一些修改。我们首先找到满足条件的最大的i,使得2^{i} \leqk,然后我们构造一个数组(长度为22)(为什么是22,因为这个解释我们考虑到2的),其中包含一下元素:

a = [k - 2^{i},k + 1,k +1 +2^{i},...,2^{19}]

要证明这个序列中不存在子序列和为k,我们考虑数组中所有不超过k的元素,因为它们是可能出现在子序列中的,这些元素是:

k-2^{i},i,2,4,...,2^{i}-1很易知,这些元素和为k-1,因此不存在子序列之和为k

为了证明对于所有的1\leq v\leq nv\neq k,都存在一个子序列的和为v,我们考虑这几种情况

  1. 如果v<2^{i},我们可以直接使用v的二进制表示作为子序列。
  2. 如果2^{i}\leq v< k,我们首先取所有不超过k的元素作为子序列的一部分,然后我们需要移除k-1-v的元素。因为2^{i}\leq v< k< 2^{i+1},所以k-1-v小于2^{i},因此我们可以直接移除它的二进制表示。
  3. 如果v>k,我们可以取k+1并加上v-k-1的二进制表示作为子序列。唯一的边界情况是当v-k-1的二进制表示中的2^{i}位被设置时,在这种情况下,我们将k+1替换成k+1+ 2^{i}

因此在所有情况下,我们都可以构造出一个子序列的和为v。

时间复杂度分析

这个算法的时间复杂度O(log_{n})

主要涉及步骤:

  1. 找到满足条件的最大值i:这一步骤的时间复杂度是O(log_{k}),因为我们需要找到满足条件的最大的j值,使得2^{i}\leq k,在一个逐渐增大的指数序列中找到这样一个值,其时间复杂度是对数级的。
  2. 构造数组:构建数组的过程中,我们从k-2^{i}开始,一直加到一些大于k的数,其时间复杂度是对数级的。
  3. 证明符合限制条件:并不涉及循环或者递归,可以看作是常数级的。

代码

t = int(input())

for tc in range(t):
    n, k = map(int, input().split())

    i = 0
    while (1 << (i + 1)) <= k:
        i = i + 1

    ans = [k - (1 << i), k + 1, k + 1 + (1 << i)]

    # 如果数组长度超过了 25,则将超出部分删除
    while len(ans) > 25:
        ans.pop()

    # 如果数组长度不足 25,则添加足够数量的数字
    for j in range(20):
        if len(ans) < 25 and j != i:
            ans.append(1 << j)

    print(len(ans))
    print(*ans)

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值