题解:CF1946D(Birthday Gift)

题解:CF1946D(Birthday Gift)

题目翻译:给定一个长度为 n n n 的数组 a a a 以及一个数 x x x,请找出最大 的 k k k,使得数组 a a a 可以被分成 k k k 个部分,并且将每个部分中所有元素异或起来的结果按位或,最终的结果小于等于 x x x。

先观察题面,发现其中出现了两个位运算------按位异或 和按位 。它们有一个共同性质,就是在二进制下每一位之间互不干扰,因此我们可以按位分别考虑 ,因此,我们可以从左往右遍历每一个二进制位

我们知道,根据或运算的规则,只要这些段中某一个段内元素异或起来为 1 1 1,则最终的结果一定 为 1 1 1,换而言之,如果现在数组中这一位为 1 1 1 的数的数量为奇数 (也就代表这一位的计算结果不可能 为 0 0 0),则最终结果一定为 1 1 1,此时,如果 x x x 的这一位为 0 0 0,则最终计算结果一定大于 x x x,所以我们的遍历需要到此为止 ,否则不需要进行任何操作,直接去往下一位;否则,即数量为偶数 (代表着这一位的计算结果可能 为 0 0 0),此时如果 x x x 这一位为 1 1 1,我们可以尝试更新答案(具体怎样更新我们稍后再说),就是代表让这一位变成 0 0 0,前面的都与 x x x 相等,此时计算结果无论后面长什么样都一定小于 x x x,当然这并不意味着我们要停止遍历,因为我们还有另一种情况 ------就是这一位选择 1 1 1,从这一位到它左边全部和 x x x 完全相同,我们不对数组进行任何改变,只是进入下一位,否则也就是 x x x 这一位为 0 0 0,那么就只能想办法让计算结果变为 0 0 0,按照对于这一位分段的方法修改数组(如何分段和修改也是稍后再讲)。通过这种方式,求出答案即可,注意如果一次答案更新都没有,就说明答案应该是 − 1 -1 −1。并且,我们不难发现,这种方法没有考虑恰好等于 x x x 的情况,因此我们先要让 x++,再进行一系列操作。

剩下的就是更新答案分段修改 了。因为我们要让 k k k 最大,也就是让越少的元素被合并越好,因此我们的分段方发如下:将所有的对应位为 1 1 1 的数分别编号, 2 n 2n 2n 号和 2 n + 1 2n+1 2n+1 号中间(包含 左右端点)的元素被分到一组,剩余的不进行合并(这就是更新答案和分段的方案)。因为这样合并恰好让两个 1 1 1 排在最左侧和最右侧,因此这一段不可以 再进行任何拆分 ,也就是说这一段的所有点可以视为一个点,这个点的值就是这一段中所有点异或起来的结果。用这种方式进行修改数组。

在实现过程中,位数用到 0 0 0 到 30 30 30 就可以了,并且我们可以用 ((a[j]>>i)&1)==1 来判断 a j a_j aj 的第 i i i 位是否为 1 1 1。

Come on!代码走起!

cpp 复制代码
#include<bits/stdc++.h>
#define N 110000
using namespace std;
int t,n,x,a[N];
int b[N];
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&x),x++;
		for(int i=1;i<=n;i++)scanf("%d",&a[i]);
		int ans=-1,cnt;
		for(int i=30;i>=0;i--){
			cnt=0;
			int zt=0;
			for(int j=1;j<=n;j++){
				if(zt==0)cnt++,b[cnt]=a[j];
				else b[cnt]^=a[j];
				if(((a[j]>>i)&1)==1)zt^=1;
			}
			if(((x>>i)&1)==1&&zt==0)ans=max(ans,cnt);
			else if(((x>>i)&1)==0&&zt==1)break;
			else if(((x>>i)&1)==0&&zt==0){
				n=cnt;
				for(int j=1;j<=cnt;j++)a[j]=b[j];
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}
相关推荐
唐叔在学习1 分钟前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
ALISHENGYA21 分钟前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法
tianmu_sama21 分钟前
[Effective C++]条款38-39 复合和private继承
开发语言·c++
chengooooooo22 分钟前
代码随想录训练营第二十七天| 贪心理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
算法·leetcode·职场和发展
jackiendsc29 分钟前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
羚羊角uou36 分钟前
【C++】优先级队列以及仿函数
开发语言·c++
姚先生9740 分钟前
LeetCode 54. 螺旋矩阵 (C++实现)
c++·leetcode·矩阵
FeboReigns42 分钟前
C++简明教程(文章要求学过一点C语言)(1)
c语言·开发语言·c++
FeboReigns1 小时前
C++简明教程(文章要求学过一点C语言)(2)
c语言·开发语言·c++
264玫瑰资源库1 小时前
从零开始C++棋牌游戏开发之第二篇:初识 C++ 游戏开发的基本架构
开发语言·c++·架构