题目链接
5.选数异或 - 蓝桥云课 (lanqiao.cn)
思路:
重要结论:a^b=x可得a^x=b
暴力做法:
每次遍历给定区间的元素,将出现的元素建一个表表示这个数在区间里出现过,同时查这个元素a的a^x是否在表里,出现则区间里能找到一对合法的数。过60%或40%数据。
注意:每次map要清空。
dp做法:
f[i]表示以i为右端点(结尾)的子数组 ,它最近的 能够选出异或为x的一对数字的 左边那个位置 为 f[i](是这个左边位置最近,不是右边位置出现得最近,因为左边位置最近的话右边位置必然在这个左边位置的右边)。
要让一个区间包括这对合法的数,那么就要让这个 区间左端点小于这对数左边数的位置,而右端点大于这对数右边数位置 。而右端点大于这对数右边数位置 这个条件,设这个dp数组的时候 作为前提满足的。 这样就找到了一个边界,如果给的以r为右端点的区间左端点小于f[r],那么 这段区间就包括了至少一对数字能够异或为x,状态转移公式为:
f[i]=max(f[i-1],p[(a[i]^x)]);
要么从前面已经找到的选,要么是自己新产生的(通过异或性质计算:a^b=x可得a^x=b,查map是否前面出现过这个a^x,出现过,就是自己产生的位置) ,
所以需要建一个 哈希表,存每个值对应的最大位置 (建表时如果有几个位置出现相同值,大的位置覆盖小的位置,因为求最近的位置,大的位置肯定更近)
代码:
暴力:
#include<bits/stdc++.h>
#define int long long
using namespace std;const int N=1e5+10;int n,m,x;
int a[N];
unordered_map<int,int> rr;
signed main(){//关同步!!!ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); cin>>n>>m>>x;for(int i=1;i<=n;i++){cin>>a[i];} while(m--){int l,r;cin>>l>>r;if(l==r) printf("no\n");else{//每次要清零 rr.clear();int i;//如果 a^b=x,那么b^x=a,a^x=b。根据这个性质,在 这个区间里用map记录(注意:每次要清空!!)//一旦当前元素b的 对应 的 b^x=a在map里有记录,说明这个区间能找到异或为x的 for(i=l;i<=r;i++){if(rr.find((a[i]^x))!=rr.end()){printf("yes\n");break;}rr[a[i]]=1;}if(i>r) printf("no\n");}}return 0;
}
dp:
#include<bits/stdc++.h>
#define int long long
using namespace std;const int N=1e5+10;int n,m,x;
int a[N];int f[N];
//注意这个哈希表的范围要根据Ai的大小开
int p[10*N+50000]={0};
signed main(){//关同步!!!ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); cin>>n>>m>>x;for(int i=1;i<=n;i++){cin>>a[i];} for(int i=1;i<=n;i++){f[i]=max(f[i-1],p[(a[i]^x)]);p[a[i]]=i;}while(m--){int l,r;cin>>l>>r;if(l==r) printf("no\n");else{if(f[r]>=l)printf("yes\n");elseprintf("no\n");}}return 0;
}