2022 蓝桥杯C++A组——选数异或

题目

在这里插入图片描述

需要知道的异或

首先说一下异或这个小可爱。
异或就是不同的返回1,相同的返回0。
比如:100100^000101
在这里插入图片描述
那么很容易得到如果是一个数和它自己异或,得到的铁定是0,(自己和自己的每个二进制位当然是一模一样的),而一个数异或0得到的肯定是它自己,因为二进制0异或0还是得到0,二进制1异或0还是得到1,所以异或0前后没有改变。
另外还有一个结论
如果a^b=x
那么a^x=b
这个结论是解决这道题的关键。推论如下:
∵ a^b=x
∴ a^b^b=x^b
又∵ 一个数异或它自己等于0,一个数异或0等于它自己
∴ a^b^b=a^0=a=x^b
∴ 如果a^b=x,那么a^x=b

解题思路

能否从某个区间 [l,r] 中选择两个数使得他们的异或等于 x ,如果[l,r]的一个子集符合条件,那么[l,r]自然也符合条件。
如果[1,3]存在这样两个数,那么[1,∞)必然也存在这两个数。换句话说,起决定因素的是子集,越小的子集决定度越精准。问题就可以转换为,求以一个数为右端口时最大的区间左端口。

这里很容易想到动态规划,不然会重复很多无用计算,而且它的数据真的庞大地不要脸。。。导致后续还需要一个我以前见都没见过的代码来辅助。。。

那么把每一个数的最大左端口算出来,用dp[r]数组来存它最大的左端口,首先让每一个数与x异或一下,就得到符合条件的数字,。但是有可能它前面有个更小的子集符合条件,所以就要存那个更小子集的左端口。
在这里插入图片描述
比如如果d^x=c,b^x=a,那么dp[d的序号]=dp[b的序号]=a的序号,看自己的左端口更大还是前面一个兄弟的左端口更大。

那么思路总结起来就是:
求a^x得到数b,(先假定a>b)那么dp[a的序号]=max(dp[a的序号-1],map[b])
map[b]就是数b的序号。

大概思路也许不难,但是代码难的一批。我最开始用了双重循环,超时超到我爆炸,然后看题解,实在佩服 算法也太难了吧 这位大佬的思路。循环一点都没浪费啊,在输入数据的那个循环被大佬用明白了。我还拘泥于用数组存。上代码吧。大佬的代码,我用我的理解来讲解。

#include<iostream>
#include<map>
using namespace std;
typedef long long ll;
const int N=1e5+5;
ll n,m,x;	
int dp[N];    
map<ll,int> mp; //输入一个数,就用键值对来存储这个数的序号
ios_base::sync_with_stdio(0);
  cin.tie(0);     //加速(很关键!)

c++的cin速度很慢,原理各位自己查,反正加上这两行就快了。
ios是iostream的缩写,base是基础,sync可以用谐音 傻样脑残 来记。

 for(int i=1;i<=n;i++){
    ll data;
    cin>>data; //输入数据
    dp[i]=max(dp[i-1],mp[data^x]);    //mp存放另一个数的序号
    mp[data]=i; //存放当前输入数据的序号,要是它能被后面的数异或得到,后面的数就会用到这个i
  }

代码

#include<iostream>
#include<map>
using namespace std;
typedef long long ll;
const int N=1e5+5;
ll n,m,x;
int dp[N];    //a^b=x  a,b中早出现的数字位置
map<ll,int> mp;
int max(int a,int b){
  return a>b?a:b;
}
int main()
{
  ios_base::sync_with_stdio(0);
  cin.tie(0);     //加速(很关键!)
  cin>>n>>m>>x;
  for(int i=1;i<=n;i++){
    ll data;
    cin>>data;
    dp[i]=max(dp[i-1],mp[data^x]);    //mp存放另一个数的位置
    mp[data]=i;
  }
  while(m--){
    int l,r;
    cin>>l>>r;
    if(dp[r]>=l) cout<<"yes\n";
    else cout<<"no\n";
  }
  return 0;
}
  • 51
    点赞
  • 85
    收藏
    觉得还不错? 一键收藏
  • 20
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值