CF1592E Bored Bakry

CF1592E Bored Bakry

题目

给定一个 n n n 个数的数组,要求找一个连续子区间,满足该子区间的区间 & \& & > > > 区间异或和。求该子区间的最大长度。

思路

一开始还在想什么数据结构,二分什么的,然后发现 & \& &有单调性而异或完全没有.

然后翻题解.

题解的意思是第 k k k位,再枚举区间,使得区间 & \& &起来后第 k k k位为 1 1 1,异或起来后第 k k k位为 0 0 0,为了保证区间 & \& &更大,我们又要强制让区间异或起来后比 k k k更高的位置为 0 0 0.

我们处理一个前缀数组 p r e i = ( a 1 ⊕ a 2 ⊕ a 3 ⊕ … ⊕ a i ) > > ( k + 1 ) pre_i=(a_1\oplus a_2\oplus a_3\oplus \ldots \oplus a_i)>>(k+1) prei=(a1a2a3ai)>>(k+1),则 p r e r ⊕ p r e l − 1 = ( a l ⊕ a l + 1 ⊕ … ⊕ a r − 1 ⊕ a r ) > > ( k + 1 ) pre_r\oplus pre_{l-1}=(a_l\oplus a_{l+1}\oplus \ldots\oplus a_{r-1}\oplus a_r)>> (k+1) prerprel1=(alal+1ar1ar)>>(k+1).

我们需要找的一个区间 [ l , r ] [l,r] [l,r],使得该区间满足以下三个条件:

  1. r − l + 1 r-l+1 rl+1,即区间长度为偶数,这就能保证异或后第 k k k位为0.
  2. 区间内所有数第 k k k为全为 1 1 1.
  3. p r e r ⊕ p r e l − 1 = 0 pre_r\oplus pre_{l-1}=0 prerprel1=0.

但是,其实要我说,第一个条件可以被第三个条件包含,只要将 p r e pre pre数组改为前 i i i个数,大于等于第 k k k位的异或和即可.

两个数异或和为 0 0 0即两个数相等,区间第 k k k位为 1 1 1可以用双指针找.

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <unordered_map>
using namespace std;
int read() {
	int re = 0;
	char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	while(c >= '0' && c <= '9') re = (re << 1) + (re << 3) + c - '0' , c = getchar();
	return re;
}
const int N = 1e6 + 10;
int n , k;
int a[N];
int ans = 0;

int pos[1 << 20];//pos[i]表示异或和为i的第一个位置为pos[i],目前没有则为-1
int main() {
	n = read();
	for(int i = 1 ; i <= n ; i++)
		a[i] = read();
	memset(pos , -1 , sizeof(pos));
	for(int k = 19 ; k >= 0 ; k--) {
		int l = 0 , r = 0;
		while(l <= n) {
			l = r + 1;
			while(l <= n && ((a[l] >> k) & 1) == 0)++l;
			r = l;
			while(r <= n && ((a[r] >> k) & 1) == 1)++r;
			--r;
			if(l > n)continue;//l,r:[l,r]内所有数的第k为均为1
			
			int sum = 0;//前缀
			pos[sum] = l - 1;
			for(int i = l ; i <= r ; i++) {
				sum ^= (a[i] >> k);
				if(pos[sum] == -1)
					pos[sum] = i;
				else
					ans = max(ans , i - pos[sum]);
			}
			sum = 0 , pos[sum] = -1;//清空
			for(int i = l ; i <= r ; i++)
				sum ^= (a[i] >> k) , pos[sum] = -1;
		}
	}
	cout << ans;
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值