题目 | Beautiful Sequence
2023牛客暑期多校训练营7
C - Beautiful Sequence
https://ac.nowcoder.com/acm/contest/57361/C
题意
给定长度为 $n$ 的序列 $\{a\}_{i=1}^n$ 的异或差分序列 $\{b\}_{i=1}^{n-1}$, 要求 $\{a\}$ 不严格递增且数字范围都在 $\left[0,2^{30}-1\right]$ 的第 $k$ 大的序列。 $1 \leq n \leq 10^6, 1 \leq k<2^{30}$ 。
题解
问题转化
首先,将 $A_{i}\oplus A_{i+1}=B_i,\;1\leq i<n$ 变形可以得到 $A_{i+1}=A_i\oplus B_i$,即得到了一个 $A_i$ 的递推式,那么:
- $A_1=A_1\oplus0$
- $A_2=A_1\oplus B_1$
- $A_3=A_1\oplus B_1\oplus B_2$
- $\vdots$
- $A_n=A_1\oplus B_1\oplus\cdots\oplus B_{n-1}$
由于 $B_i$ 已经给出,那么说明只要确定了 $A_1$,那么 $A_1\sim A_n$ 就全部确定了。那么题目让我们求字典序第 $k$ 小的 $A$ 序列,就相当于求出第 $k$ 小的合法 $A_1$ 了。
为了方便,令 $C$ 为 $B$ 的前缀异或序列,即 $C_i=B_1\oplus B_2\oplus\cdots\oplus B_{i-1}$. 特别的令 $C_1=0$. 这样,$A$ 序列可以写成:
- $A_1=A_1\oplus C_1$
- $A_2=A_1\oplus C_2$
- $\vdots$
- $A_n=A_1\oplus C_n$
根据限制 $A_1\leq A_2\leq\cdots\leq A_n$,那么就可以转换为:
$$ A_1\oplus C_1\leq A_1\oplus C_2\leq\dots\leq A_1\oplus C_n $$
综上,本题的问题就是要求解出满足上面这个式子的第 $k$ 小的 $A_1$. 为了方便,下文的 $A_1$ 全部用 $x$ 表示。
问题求解
首先考虑一个子问题:$x\oplus C_{i}\leq x\oplus C_{i+1}$
如果 $C_i$ 和 $C_{i+1}$ 的二进制最高的不同位是第 $k$ 位,记这一位的值是 $C_{i,k}$
- $C_{i,k}=0,C_{i+1,k}=1$,那么 $x_{k}$ 必须取 $0$. ($0\oplus0\leq0\oplus1$)
- $C_{i,k}=1,C_{i+1,k}=0$,那么 $x_k$ 必须取 $1$.($1\oplus1\leq1\oplus0$)
- $C_{i,k}=C_{i+1,k}$,那么 $x_k$ 可以任取 $0/1$(相当于待定)
由于考虑的是二进制最高位,那么这一位满足了 $\leq$,那么原问题也是满足的,如果这一位不满足,那么原问题就是不满足的。
接下来考虑整个问题:$x\oplus C_1\leq x\oplus C_2\leq\dots\leq x\oplus C_n$
可以从左到右链式求解,仍然是用上面的求解方式确定 $x$ 的某一位。可以首先将 $x$ 每一位记做 $-1$ 表示待定,然后从左到右根据限制进行填写。如果取 $0$ 就将 $-1$ 改为 $0$,如果取 $1$ 就将 $-1$ 改为 $1$,如果都能取就保留 $-1$ 表示待定。
不过此时就可能出现冲突,比如 $x_k$ 这一位在之前已经填上了 $0$,但是现在又要填 $1$,这就代表了矛盾,即这种情况下 $x$ 无解,直接输出 $-1$ 即可。
如果有解的话,就会解出一个 $x$,不过这个 $x$ 的某些位可能还保留着 $-1$ 表示待定,需要将 $-1$ 填上数值。由于题目要求的是第 $k$ 小的 $x$,那么就将 $k-1$ 转为二进制,对应着填入 $x$ 中的 $-1$ 位里。例如解出来 $x$ 的二进制位情况:$\underline\ ,\cdots,\underline\ ,0,\underline\ ,1,0,\underline\ $,如果 $k=3$,则填好后的 $x$ 为:$\underline0,\cdots,\underline0,0,\underline1,1,0,\underline0$.
如果 $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;
}
本文采用 CC BY-SA 4.0 许可,本文 Markdown 源码:Haotian-BiJi