题目描述
给定一个长度为 nn 的数列 A_{1}, A_{2}, \cdots, A_{n}A1,A2,⋯,An 和一个非负整数 xx, 给定 mm 次查询, 每次询问能否从某个区间 [l, r][l,r] 中选择两个数使得他们的异或等于 xx 。
输入格式
输入的第一行包含三个整数 n, m, xn,m,x 。
第二行包含 nn 个整数 A_{1}, A_{2}, \cdots, A_{n}A1,A2,⋯,An。
接下来 mm 行,每行包含两个整数 l_{i}, r_{i}li,ri 表示询问区间 \left[l_{i}, r_{i}\right][li,ri] 。
输出格式
对于每个询问, 如果该区间内存在两个数的异或为 xx 则输出 yes
, 否则输出 no
。
输入输出样例
输入 #1复制
4 4 1 1 2 3 4 1 4 1 2 2 3 3 3
输出 #1复制
yes no yes no
说明/提示
【样例说明】
显然整个数列中只有 2,3 的异或为 1 。
【评测用例规模与约定】
对于 20 \%20% 的评测用例, 1 \leq n, m \leq 1001≤n,m≤100;
对于 40 \%40% 的评测用例, 1 \leq n, m \leq 10001≤n,m≤1000;
对于所有评测用例, 1 \leq n, m \leq 10^5,0 \leq x<2^{20}, 1 \leq l_{i} \leq r_{i} \leq n1≤n,m≤105,0≤x<220,1≤li≤ri≤n , 0 \leq A_{i}<2^{20}0≤Ai<220 。
蓝桥杯 2022 省赛 A 组 D 题。
x=1时,2 xor 3 =1, 6 xor 7 =1 数对为(2,3) (6,7)...依次类推
使用动态规划,dp[i]保存1~i的区间范围内数对的最大左下标。
Last[a]保存的是a的下标。
动态转移方程为dp[i]=max(dp[i-1],Last[a*x]);
当 l 小于dp[r](即在1~r的范围内,数对的最大左下标时,此时包含至少一个数对) 为yes,否则为no
package lan;
import java.util.*;
public class lan9 {
public static void main(String []args)
{
Scanner in=new Scanner(System.in);
int n,m;
int x;//A[]记录数列
int l,r;
//dp[i]保存区间在1~i中数对的最大左下界
int dp[];
int A[];
int check[];//1表示yes,0表示no
HashMap<Integer,Integer> Last=new HashMap<Integer,Integer>();
n=in.nextInt();
m=in.nextInt();
x=in.nextInt();
dp=new int[n+1];
A=new int[n+1];
check=new int[n+1];
dp[0]=0;
for(int i=1;i<=n;i++)
{
A[i]=in.nextInt();
Last.put(A[i], i);//A[i]最后一次出现的下标
if(Last.get(A[i]^x)==null) dp[i]=dp[i-1];
else dp[i]=Math.max(dp[i-1], Last.get(A[i]^x));
}
for(int i=1;i<=m;i++)
{
l=in.nextInt();
r=in.nextInt();
if(dp[r]>=l) check[i]=1;
else check[i]=0;
}
for(int i=1;i<=m;i++)
if(check[i]==1)System.out.println("yes");
else System.out.println("no");
}
}
更新题解:使用线段树的方法
线段树每个节点表示一个区间,以及带有该区间的一个特殊值;
同时每个树节点满足 tree(l,r)=tree(l,mid)+tree(mid+1,r)
import java.util.*;
public class pro11{
static final int maxn=10000+20;
static int Left[]=new int[maxn];
static int num[]=new int[maxn];
static int tree[]=new int[maxn];
static int a[]=new int[maxn],pos[]=new int[maxn];
static int n,m,x;
public static void main(String args[])
{
Scanner in=new Scanner(System.in);
n=in.nextInt();
m=in.nextInt();
x=in.nextInt();
for(int i=1;i<=n;i++)
{
a[i]=in.nextInt();
Left[i]=pos[a[i]^x];//满足异或的左边界限的位置
pos[a[i]]=i;
}
build(1,1,n);
while((m--)!=0)
{
int l,r;
l=in.nextInt();
r=in.nextInt();
if(query(1,1,n,l,r)>=l)
System.out.println("yes");
else System.out.println("no");
}
}
public static void build(int o,int l,int r)
{
if(l==r) {
tree[o]=Left[l];//第o个树节点在区间(l,r)满足异或条件的最左边位置
return;
}
int mid=(r+l)/2;
build(o<<1,l,mid);//左子树
build(o<<1|1,mid+1,r);
tree[o]=Math.max(tree[o<<1], tree[o<<1|1]);
}
public static int query(int o,int l,int r,int left,int right)
{
if(l>=left&&r<=right) return tree[o];//返回满足区间(right,left)的树结点值,(right,left)包含区间(l,r)
int mid=(l+r)/2;
int ans=0;
if(mid>=left) ans=Math.max(query(o<<1,l,mid,left,right),ans);
if(right>mid) ans=Math.max(query(o<<1|1,mid+1,r,left,right), ans);
return ans;
}
}