登录—专业IT笔试面试备考平台_牛客网
题目大意:给出一长度n-1的数组b,要求构造一个长度为n的数组a,满足a[i]^a[i+1]=b[i],a[i]<=a[i+1],求字典序第k小的数组
1<=n<=1e6;0<=a[i],b[i]<=
思路:因为a[1]^a[2]=b[1],a[2]^a[3]=b[2]...a[i]^a[i+1]=b[i],我么对整个式子两边求异或和可得a[1]^a[i+1]=sum[i],其中sum[i]表示b的异或前缀和,所以只要我们确定了a[1],就能确定所有的a[i],那么我们考察每个a[i]怎么构造,使其满足递增的条件。
要递增,a[i]和a[i+1]的最高位要么应该相同,要么a[i]是0,a[i+1]是1,所以我们先找到a[i]和a[i+1]最高的不同位,也就相当于找sum[i]和sum[i-1]的最高不同位,如果sum[i]最高位=1,那么a[1]这一位应该是0,这样a[i+1]这一位就是1,如果sum[i]最高位=0,那么a[1]这一位应该是1,这样a[i+1]这一位就是1,而如果这一位的情况之前已经确定过了,且当前情况与之前的不符,那么就输出-1。
然后再考虑第k小,我们直接把k二进制拆分,然后依次填入a[1]中没有被确定的位置即可,最后有a[1]再构造除整个a数组,期间注意判断a[i]的范围有没有超过
//#include<__msvc_all_public_headers.hpp>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
ll b[N];
ll sum[N];
ll ans[N];
void solve()
{
ll n, k;
cin >> n >> k;
for (int i = 1; i < n; i++)
{
cin >> b[i];
sum[i] = sum[i - 1] ^ b[i];//求前缀异或和
}
vector<bool>a(60), vis(60);
for (int i = 1; i <= n-1; i++)
{//枚举每一个a[i+1]
for (int j = 29; j >= 0; j--)
{//找最高不同位
if ((sum[i] >> j & 1) != (sum[i - 1] >> j & 1))
{
int temp = sum[i] >> j & 1 ^ 1;//a[1]的这一位应该与sum[i]取反
if (vis[j] && temp != a[j])
{
cout << -1 << endl;
return;
}
a[j] = temp;
vis[j] = 1;
break;
}
}
}
k--;
int dig = 0;
while (k)
{//将k的二进制表达插入a[1]中
while (vis[dig])
dig++;
int x = k & 1;
a[dig] = x;
dig++;
k >>= 1;
}
ans[1] = 0;
for (int i = 30; i >= 0; i--)
{//有a[1]的二进制求出其十进制表达
ans[1] *= 2;
ans[1] += a[i];
}
for (int i = 2; i <= n; i++)
{
ans[i] = ans[i - 1] ^ b[i - 1];
if (ans[i] > 1 << 30)
{//判断得到的a[i]在不在给定范围内
cout << -1 << endl;
return;
}
}
for (int i = 1; i <= n; i++)
{
cout << ans[i] << " ";
}
cout << endl;
}
int main()
{
int t;
cin >> t;
while (t--)
{
solve();
}
}