2023牛客暑期多校训练营7

C - Beautiful Sequence

https://ac.nowcoder.com/acm/contest/57361/C

题意

给定长度为 n 的序列 {a}i=1n 的异或差分序列 {b}i=1n1, 要求 {a} 不严格递增且数字范围都在 [0,2301] 的第 k 大的序列。 1n106,1k<230

题解

问题转化

首先,将 AiAi+1=Bi,1i<n 变形可以得到 Ai+1=AiBi,即得到了一个 Ai 的递推式,那么:

  • A1=A10
  • A2=A1B1
  • A3=A1B1B2
  • An=A1B1Bn1

由于 Bi 已经给出,那么说明只要确定了 A1,那么 A1An 就全部确定了。那么题目让我们求字典序第 k 小的 A 序列,就相当于求出第 k 小的合法 A1 了。

为了方便,令 CB 的前缀异或序列,即 Ci=B1B2Bi1. 特别的令 C1=0. 这样,A 序列可以写成:

  • A1=A1C1
  • A2=A1C2
  • An=A1Cn

根据限制 A1A2An,那么就可以转换为:

A1C1A1C2A1Cn

综上,本题的问题就是要求解出满足上面这个式子的第 k 小的 A1. 为了方便,下文的 A1 全部用 x 表示。

问题求解

首先考虑一个子问题:xCixCi+1

如果 CiCi+1 的二进制最高的不同位是第 k 位,记这一位的值是 Ci,k

  • Ci,k=0,Ci+1,k=1,那么 xk 必须取 0. (0001
  • Ci,k=1,Ci+1,k=0,那么 xk 必须取 1.(1110
  • Ci,k=Ci+1,k,那么 xk 可以任取 0/1(相当于待定)

由于考虑的是二进制最高位,那么这一位满足了 ,那么原问题也是满足的,如果这一位不满足,那么原问题就是不满足的。

接下来考虑整个问题:xC1xC2xCn

可以从左到右链式求解,仍然是用上面的求解方式确定 x 的某一位。可以首先将 x 每一位记做 1 表示待定,然后从左到右根据限制进行填写。如果取 0 就将 1 改为 0,如果取 1 就将 1 改为 1,如果都能取就保留 1 表示待定。

不过此时就可能出现冲突,比如 xk 这一位在之前已经填上了 0,但是现在又要填 1,这就代表了矛盾,即这种情况下 x 无解,直接输出 1 即可。

如果有解的话,就会解出一个 x,不过这个 x 的某些位可能还保留着 1 表示待定,需要将 1 填上数值。由于题目要求的是第 k 小的 x,那么就将 k1 转为二进制,对应着填入 x 中的 1 位里。例如解出来 x 的二进制位情况: ,, ,0, ,1,0, ,如果 k=3,则填好后的 x 为:0,,0,0,1,1,0,0.

如果 k 的大小过大,x 中的空位根本填不下,那么说明这种情况下无解,直接输出 1 即可。

代码

#include <bits/stdc++.h>
#define endl '\n'
// #define int long long

using namespace std;

void solve()
{
    int n, k;
    cin >> n >> k;
    vector<int> b(n + 10), pxor(n + 10);
    for (int i = 1; i < n; i++)
    {
        cin >> b[i];
        pxor[i] = pxor[i - 1] ^ b[i];
    }
    vector<int> a1_digit(30, -1);
    for (int i = 1; i < n; i++)
    {
        for (int j = 29; j >= 0; j--)
        {
            int digit_1 = pxor[i - 1] >> j & 1;
            int digit_2 = pxor[i] >> j & 1;
            if (digit_1 != digit_2)
            {
                if (a1_digit[j] == -1)
                {
                    a1_digit[j] = digit_1;
                }
                else if (a1_digit[j] != digit_1)
                {
                    cout << -1 << endl;
                    return;
                }
                break;
            }
        }
    }
    k--;
    for (int i = 0; i <= 29; i++)
    {
        if (a1_digit[i] == -1)
        {
            a1_digit[i] = k & 1;
            k >>= 1;
        }
    }
    if (k)
    {
        cout << -1 << endl;
        return;
    }
    int a1 = 0;
    for (int i = 29; i >= 0; i--)
    {
        a1 = (a1 << 1) + a1_digit[i];
    }
    cout << a1 << ' ';
    for (int i = 1; i < n; i++)
    {
        a1 ^= b[i];
        cout << a1 << ' ';
    }
    cout << endl;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}