【Codeforces】Codeforces Round #832 (Div. 2) D. Yet Another Problem | 预处理、分类讨论

189 阅读3分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

题目链接

Problem - D - Codeforces

题目

image.png

题目大意

给定一个长度为 nn 的数组 a1,a2,...,ana_1,a_2,...,a_n,有 qq 个询问,每次询问对于给定的区间 [l,r][l,r],能执行若干次下述操作:

  • 选择两个正整数 lLRrl\le L \le R\le r,且 RL+1R-L+1 是奇数,将 aL,aL+1,...,aRa_L,a_{L+1},...,a_R 全部替换为他们的异或和,即 (aLaL+1...aR)(a_L\oplus a_{L+1}\oplus ...\oplus a_R)

问是否使得 al,al+1,...,ara_l,a_{l+1},...,a_r 全部变成 0,如果不能就输出 -1,如果能就输出将 al,al+1,...,ara_l,a_{l+1},...,a_r 全部变成 0 需要的最小操作次数。每次询问相互独立。

思路

对于每一个询问:

  1. 显然 (alal+1...ar)0(a_l\oplus a_{l+1}\oplus ...\oplus a_r)\neq 0 的话,不可能通过上述操作使得 al,al+1,...,ara_l,a_{l+1},...,a_r 全部变成 0,我们应该输出 -1。
  2. 如果被询问的整个区间已经全部都是 0 了,我们无需进行操作即可满足条件,所以应该输出 0。
  3. 又因为 (alal+1...ar)0(a_l\oplus a_{l+1}\oplus ...\oplus a_r)\neq 0,如果 rl+1r-l+1 是奇数,我们可以直接选择区间 l,rl,r 执行一次操作即可达成目的,输出 1。
  4. 现在我们的区间长度为偶数了,如果 al=0a_l=0 或者 ar=0a_r=0 的话,我们可以无视掉为 0 的端点,对剩下长度为奇数的区间进行一次操作,输出 1。
  5. 容易发现,如果存在以 xx 满足 l<x<rl<x<rrx+1r-x+1 是奇数,使得 (axax+1...ar)=0(a_x\oplus a_{x+1}\oplus ...\oplus a_r)= 0,显然从 llx1x-1 这一段区间的长度也为奇数且异或和也为 0,我们可以通过 2 次操作达成目的,输出 2。
  6. 若不存在 xx 满足条件 5,则无解,输出 -1。

其中条件 1 可以通过预处理前缀异或和解决,条件 2 可以通过维护前缀和来解决,条件 3 和 4 也可以直接判断,我们来说明条件 5 的判断方法。

首先我们可以预处理出以 ii 为右端点的区间中的一个左段点 xx,满足:

  • xix\le i
  • ix+1i-x+1 是奇数
  • axax+1...ai=0a_x\oplus a_{x+1}\oplus ... \oplus a_i=0
  • xx 是满足上述条件的最大值

这个东西只需要对奇偶下标分别开个 map 记录前缀异或和的值的对应的下标,然后每个位置在另一个 map 里找与之前缀异或和相等的位置再 +1 即可。显然这样做可以求出我们想要的东西。

在查询中我们只需要判断 ll 和以 rr 为右端点的最大的使得区间长度为奇数且区间异或和为 0 的左端点之间的大小关系即可。

代码

#include <bits/stdc++.h>
#define nnn printf("No\n")
#define yyy printf("Yes\n")
using namespace std;
using LL=long long;
const int N=500001;
int n,m;
map<int,int> mp0,mp1;
int xsum[N],a[N],lneq0[N],lnx0[N];
int getans(int l,int r)
{
	if ((xsum[r]^xsum[l-1])!=0) return -1;
	if (lneq0[r]<l) return 0;
	if ((r-l+1)&1) return 1;
	if (a[l]==0||a[r]==0) return 1;
	if (lnx0[r]>l) return 2;
	return -1;
}
int main()
{
	scanf("%d%d",&n,&m);
	int id=0;
	for (int i=1;i<=n;++i)
	{
		scanf("%d",&a[i]);
		xsum[i]=xsum[i-1]^a[i];
		if (a[i]) id=i;
		lneq0[i]=id;
		if (i%2)
		{
			mp1[xsum[i]]=i;
			lnx0[i]=mp0[xsum[i]];
		}
		else
		{
			mp0[xsum[i]]=i;
			lnx0[i]=mp1[xsum[i]];
		}
	}
	int l,r;
	for (int i=1;i<=m;++i)
	{
		scanf("%d%d",&l,&r);
		printf("%d\n",getans(l,r));
	}
	return 0;
}