题目链接: https://www.acwing.com/problem/content/description/4648/
题目描述:
评价: 这道题感觉还是蛮有意思的,难度适中,而且有一定的思维含量,值得反复品味。
思路: 首先我们定义一个数组g[N], 其中的每个元素g[i] 表示在所有 i<j<=n 的j中,满足a[j] ^ a[i] = x的最小的那个j. 这是因为如果存在多个这样的j, 最小的那个j一定是最优的,因为他更加容易被包含在一个区间[l,r] 中.
假设我们已经得到了上面的数组g, 那么实际上题目的问题就转化为: 每次给定一个查询[l,r] ,询问所有 l=< i <=r 的 i 是否存在一个g[i] <= r. 进一步,我们将这个问题转化为:
询问数组g在区间[l,r] 上的最小值是否小于等于r. 这是一个区间查询问题,且这个问题是满足区间可加性的,可以用线段树解决。
那么只需要考虑数组g 是如何构建的,注意到,若给定a, 满足 a^b=c 的b的取值是唯一的(等式两边异或上a就不难证明),因此,对于每一个位置i,满足t ^ a[i] =x 的值t是唯一的,我们只需要知道数组中数值 t 出现的全部位置即可; 另一方面,由于数组g的定义,因此需要动态维护每个数值出现的索引(要保证后面的位置不能够使用前面的位置的信息),因此可以用一个队列(先进先出)来进行维护。
参考代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<unordered_map>
#define ll long long
using namespace std;
const int N=1e5+10;
const int inf=1e9+7;
int n,m,x;
int a[N];
int g[N];
unordered_map<int,int> hashs;
queue<int> q[N];
int cnt=0;struct node{int l;int r;int minnum;
};
node tree[4*N];
void push_up(int cur){tree[cur].minnum=min(tree[cur<<1].minnum,tree[cur<<1|1].minnum);return ;
}
void build(int cur,int l,int r){tree[cur].l=l;tree[cur].r=r;if(l==r) {tree[cur].minnum=g[l];return ;}int mid=l+r>>1;build(cur<<1,l,mid);build(cur<<1|1,mid+1,r);push_up(cur);return ;
}int gets(int cur,int l,int r){if(tree[cur].l>=l&&tree[cur].r<=r){return tree[cur].minnum;}int mid=tree[cur].l+tree[cur].r>>1;int res1=inf;int res2=inf;if(l<=mid) res1=gets(cur<<1,l,r);if(r>=mid+1) res2=gets(cur<<1|1,l,r);int res=min(res1,res2);return res;
}int main(void){scanf("%d%d%d",&n,&m,&x);for(int i=1;i<=n;i++){scanf("%d",&a[i]);if(hashs[a[i]]==0){hashs[a[i]]=++cnt;}q[hashs[a[i]]].push(i);}for(int i=1;i<=n;i++){int tmp=x^a[i];q[hashs[a[i]]].pop();if(hashs[tmp]==0) g[i]=n+1;else{if(q[hashs[tmp]].empty()) g[i]=n+1;else g[i]=q[hashs[tmp]].front();}}build(1,1,n);for(int i=1;i<=m;i++){int l,r;scanf("%d%d",&l,&r);int res=gets(1,l,r);if(res<=r) printf("yes\n");else printf("no\n");}return 0;
}