选数异或(BP思想)

题目 2665: 

蓝桥杯2022年第十三届省赛真题-选数异或

时间限制: 3s 内存限制: 320MB

题目描述

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

输入格式

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

第二行包含 n 个整数 A1, A2, · · · , An 。

接下来 m 行,每行包含两个整数 li ,ri 表示询问区间 [li ,ri ] 。

输出格式

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

样例输入

复制

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

样例输出

复制

yes
no
yes
no

提示

显然整个数列中只有 2, 3 的异或为 1。

对于 20% 的评测用例,1 ≤ n, m ≤ 100;

对于 40% 的评测用例,1 ≤ n, m ≤ 1000;

对于所有评测用例,1 ≤ n, m ≤ 100000 ,0 ≤ x < 220 ,1 ≤ li ≤ ri ≤ n , 0 ≤ Ai < 220。 

解题思路:

        我们要把其转化为一个求范围的思路。我们知道ab=x,则a⊕x=b,将其转化为求解b,然而将求解找到b又转化为找到了比 l 数字位置大的就行了。

        假设有 a1 a2 a3 a4 a5几个数,其范围l=3,r=5,异或值为 x ,我们就可以求解a5⊕x=a4,a5⊕x=a3,a5⊕x=a2,只要存储离r最大的一个边界值就行,比如是4,然后我们看存储数字的数组f[r]>=l,就说明其是yes。

        使用一个数组f[i]记录左边的、与第i个数配对的数的下标为f[i],此时,f[i]<i恒成立问题转化为:在L<=i<=R中,找到一个i满足f[i]>=L现在,我们固定i为R,问题再次转化:在区间[1,R)中找到一个j满足f[j]>=L只要我们找到这样的j,那么就可以知道,在区间[L,R]中至少存在一对数配对虽然刚才说固定i为R,但其实i不是真的等于R,实际做法是:对于第i个位置,我们求出区间[1,i)内最大的f[i],假设用g数组来存当询问到区间[L,R]时,我们判断等式f[R]>=L是否成立,若成立说明存在一个下标f[i]>=L,且i<=R,因为我们在求g[R]的时候并没有用到下标大于R的值此时就说明有配对。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int a[N],f[N],t[N];
int main(){
	int n,m,x;
	cin>>n>>m>>x;
	for(int i=1;i<=n;i++)	scanf("%d",&a[i]); 
	for(int i=1;i<=n;i++){		//求一个坐标是他离r最近,且它能异或求解到另外一个数 
		f[i]=max(f[i-1],t[a[i]^x]);		//求左前缀最大值,也就是最近的一个坐标 
		t[a[i]]=i; 			//用于标记该值放在数组那个位置 
	}
	while(m--){
		int l,r;
		scanf("%d%d",&l,&r);
		if(f[r]>=l)			//如果这个数大于l就是可以异或 
			cout<<"yes\n";
		else 
			cout<<"no\n";
	}	
	return 0;
}

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值