第十三届蓝桥杯省赛C++A组 D 题、C++C组 F 题、Java C 组 F 题——选数异或 (AC)

1.选数异或

1.题目描述

给定一个长度为 n n n 的数列 A 1 , A 2 , ⋯   , A n A_{1}, A_{2}, \cdots, A_{n} A1,A2,,An​和一个非负整数 x x x, 给定 m m m 次查 询, 每次询问能否从某个区间 [ l , r ] [l, r] [l,r] 中选择两个数使得他们的异或等于 x x x

2.输入格式

输入的第一行包含三个整数 n , m , x n, m, x n,m,x

第二行包含 n n n 个整数 A 1 , A 2 , ⋯   , A n A_{1}, A_{2}, \cdots, A_{n} A1,A2,,An 。接下来 m m m 行,每行包含两个整数 l i , r i l_{i}, r_{i} li,ri表示询问区间 [ l i , r i ] \left[l_{i}, r_{i}\right] [li,ri]

3.输出格式

对于每个询问, 如果该区间内存在两个数的异或为 x x x 则输出 yes, 否则输出 no

4.样例输入

4 4 1
1 2 3 4
1 4
1 2
2 3
3 3

5.样例输出

yes
no
yes
no

6.数据范围

1 ≤ n , m ≤ 100000 , 0 ≤ x < 2 20 , 1 ≤ l i ≤ r i ≤ n , 0 ≤ A i < 2 20 1≤n,m≤100000,0≤x<2^{20},1≤li≤ri ≤n, 0 \leq A_{i}<2^{20} 1n,m100000,0x<220,1lirin,0Ai<220

7.原题链接

选数异或

2.解题思路

对于异或的性质不过多在这讲解,利用的也是非常简单的公式:如果存在a^b=x,那么一定满足a^x=bb^x=a。也就是说,对于一个整数 a ,有且只存在一个数b使得其和a异或得到x,我们可以通过a^x来得到这个数的值。

对此,我们可以处理得到数组中所有符合的数对 ( a , b ) (a,b) (a,b),其中 a a a 是数组中靠左的数, b b b是靠右边的。我们可以枚举 b b b , 然后判断在数组前面是否出现过数 a a a ,这一步我们可以使用哈希表记录维护每个数最后出现的位置,如果存在则说明找到一对符合的数对。那我们该如何记录答案呢?

我们定义 f [ i ] f[i] f[i]为前 i i i 个数出现数对中 a a a最大下标。为什么要这么设计呢?
考虑查询时需要确保 ( a , b ) (a,b) (a,b)都在查询区间 [ l , r ] [l,r] [l,r]中,由于只需要考虑是否存在任意一对,那我们只需要维护最有可能的一对。当我们查询 f [ r ] f[r] f[r] 时,可以保证查询到的所有 b b b 下标 一定不超过 r r r。那么此时就只需要保证是否存在一个 a a a 满足它的下标大于 l l l 即可。那么这样我们对于每个查询 [ l , r ] [l,r] [l,r],只需要去判断 f [ r ] > = l f[r]>=l f[r]>=l 即可,符合则输出yes,否则输出no

当然我们需要考虑如何转移,对于当前的值 v v v ,下标为 i i i。如果哈希表中不存在数 x^v,则 f [ i ] = f [ i − 1 ] f[i]=f[i-1] f[i]=f[i1],如果存在则为 f [ i ] = m a x ( f [ i − 1 ] , m [ v ˆ x ] ) f[i]=max(f[i-1],m[v ˆx]) f[i]=max(f[i1],m[vˆx])。因为 C++ 的map对不存在的数默认输出0,所以不影响我们的答案,即转移方程为:
f [ i ] = m a x ( f [ i − 1 ] , m [ v ˆ x ] ) f[i]=max(f[i-1],m[v ˆx]) f[i]=max(f[i1],m[vˆx])

3.Ac_code

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int, int> PII;
#define pb(s) push_back(s);
#define SZ(s) ((int)s.size());
#define ms(s,x) memset(s, x, sizeof(s))
#define all(s) s.begin(),s.end()
const int inf = 0x3f3f3f3f;
const int mod = 1000000007;
const int N = 200010;

int n, q, x;
map<int, int> m;
int f[N];
void solve()
{
	cin >> n >> q >> x;
	for (int i = 1; i <= n; ++i) {
		int v;
		cin >> v;
		f[i] = max(f[i - 1], m[x ^ v]);
		m[v] = i;
	}
	while (q--) {
		int l, r;
		cin >> l >> r;
		if (f[r] >= l) cout << "yes" << '\n';
		else cout << "no" << '\n';
	}
}
int main()
{
	ios_base :: sync_with_stdio(false);
	cin.tie(nullptr);
	int t = 1;
	while (t--)
	{
		solve();
	}
	return 0;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

执 梗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值