CF1968F 题解

倒数第一分钟提交成功,同时带我在 CF 的第 55 场比赛中上青,留念。

思路

异或的性质:

  1. xx=0x\oplus x=0
  2. ab=baa\oplus b=b\oplus a
  3. xy=zx\oplus y=z,则 yz=x,xz=yy\oplus z = x,x\oplus z=y

这题首先想到做一个前缀异或和,设 si=a1a2ai,S=alal+1ar1ar=srsl1s_i=a_1\oplus a_2\oplus\dots\oplus a_i,S=a_l\oplus a_{l+1}\oplus\dots\oplus a_{r-1}\oplus a_r=s_r\oplus s_{l-1},若 S=0S=0,则一定有解,因为 ala_l 一组,剩下的数一组即可。不然就一定得是奇数组,且每组的异或和都是 SS,若是偶数组 SS 就一定是 00 了。

接下来我们证明若可以分为奇数组数,则一定可以分为 33 组数:设 alara_l\sim a_r 可以分为 xx 组数,每组的异或和都是 SS,则把任意 33 组连续的数合并为一组,结果不变,依然成立。

此时我们只要寻找两个分界点即可。定义一些 vector 数组 vvviv_i 里存的是 Sj=iS_j=i 时,递增的所有 jj。第一段 alax1a_l\sim a_{x_1},这一段的异或和是 SS,所以 sx1=sl1S=srs_{x_1}=s_{l-1}\oplus S=s_r,我们贪心地在 vsrv_{s_r} 中二分找到第一个 l\ge l 的下标作为 x1x_1,若找不到或 x1rx_1\ge r 则无解;然后继续二分找第二三段的分界线,我们又在 vsl1v_{s_{l-1}} 中找到第一个 >x1>x_1 的数作为 x2x_2,如果找不到或 x2rx_2\ge r 则无解(此处 r\ge r 而不是 >r>r 是因为第三段不能为空)。其它情况就有解了!

代码

# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair <int, int> pii;
int t, n, q, l, r, x, tmp, a[200005], tot;
map <int, int> mp;
vector <int> v[200005];
int main () {
	ios::sync_with_stdio (0);
	cin.tie (0);
	cout.tie (0);
	cin >> t;
	while (t --) {
		cin >> n >> q;
		tot = 0, mp.clear ();
		for (int i = 1; i <= n; ++ i) {
			v[i].clear ();
			cin >> a[i], a[i] ^= a[i - 1];
			if (! mp[a[i]])
				mp[a[i]] = ++ tot;
			v[mp[a[i]]].emplace_back (i);
		}
		while (q --) {
			cin >> l >> r;
			if (! (a[r] ^ a[l - 1])) {
				cout << "YES\n";
				continue ;
			}
			tmp = mp[a[r]];
			x = lower_bound (v[tmp].begin (), v[tmp].end (), l) - v[tmp].begin ();
			if (x >= v[tmp].size () || v[tmp][x] >= r) {
				cout << "NO\n";
				continue ;
			}
			tmp = mp[a[l - 1]];
			x = lower_bound (v[tmp].begin (), v[tmp].end (), v[mp[a[r]]][x]) - v[tmp].begin ();
			if (x >= v[tmp].size () || v[tmp][x] >= r) {
				cout << "NO\n";
				continue ;
			}
			cout << "YES\n";
		}
		cout << '\n';
	}
	return 0;
}
posted @ 2024-05-04 17:41  Vitamin_B  阅读(6)  评论(0)    收藏  举报  来源