D. Yet Another Problem(异或/位运算/二分)

题目

题意

给定n个数a,q个查询,对于每次查询,给定下标1<=l<=r<=n,

问经过若干以下操作,能否使得a[l],a[l+1],…,a[r]全部变成0

  • 选择区间l<=L,R<=r,
  • 选择的区间,长度一定要为奇数
  • 将区间[L,R]所有元素,更新为它们的异或和,即a[L]=a[L+1]=…a[R]=a[L]^a[L+1]^…^a[R]

问,需要经过最少多少次操作,才能使a[l],a[l+1],…,a[r]全部变成0。如果不能使a[l],a[l+1],…,a[r]全部变成0,则输出-1

思路

每次操作,不影响区间[l,r]的异或和。

  • 如果a[l]^a[l+1]^…^a[r]不等于0,此时显然无解
  • 如果查询的区间,所有元素为0,此时有解,操作次数为0
  • 如果查询的区间长度为奇数,此时只需要整个区间异或1次

现在考虑区间长度为偶数的场景。

观察操作后的异或前缀和,定义s为数组的异或前缀和元素集合。
那么经过一次操作后,得到的新集合s’ 元素只减不增,即s’是s的子集。

要使得最终所有元素为0,即最终s’={0},则要求原来的前缀和集合s包含0。

因此,我们可以维护下标为奇数的和下标为偶数的异或前缀和,对应的下标数组。
对于每次查询,查询和l-1奇偶性相同的前缀和下标中,是否存在对应的下标j,使得a[l]^a[l+1]^…^a[j]==0
如果能找到,我们只需选择区间[l,j],[j+1,r],执行2次操作,即可。

特判下,a[l]为0和a[r]为0的情况。

详见代码

代码

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pcc pair<char, char>
#define pii pair<int, int>
#define inf 0x3f3f3f3f
const int maxn = 200010;
const int mod = 998244353;


int n, q;
int a[maxn];
map<int, vector<int>> mp[2];
int b[maxn];
ll sum[maxn];
int l, r;

void solve() {
    scanf("%d%d", &n, &q);
    sum[0] = 0;
    for (int i = 1; i <= n; ++i) {
    	scanf("%d", &a[i]);
    	b[i] = b[i-1] ^ a[i];
    	mp[i&1][b[i]].push_back(i);
    	sum[i] = sum[i-1] + a[i];
	}
	
	while (q--) {
		scanf("%d%d", &l, &r);
		int len = r - l + 1;
		if (b[l-1] ^ b[r]) { // a[l]^...^a[r] != 0
			printf("-1\n");
			continue;
		}
		
		int res = -1;
		if (sum[r] - sum[l-1] == 0) { // a[l] =... = a[r] = 0
			res = 0;
		} else if ((len & 1) || (!a[l] || !a[r])) {
			res = 1;
		} else {
			vector<int> &ve = mp[l&1][b[l-1]];
			int idx = lower_bound(ve.begin(), ve.end(), l) - ve.begin();
			if (idx < ve.size() && ve[idx] <= r) {
				res = 2;
			}
		}
		printf("%d\n", res);
		
	}
}
int main() {
    int t = 1;
//    scanf("%d", &t);
    int cas = 1;
    while (t--) {
//		printf("cas %d:\n", cas++);
        solve();
    }

}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值