Beautiful Sequence 2023牛客暑期多校训练营7 C

登录—专业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]<=2^{30}

思路:因为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]的范围有没有超过2^{30}

//#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();
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

timidcatt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值