目录
题目:
题目描述
给定一个长度为 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。
三份题解:
题解一(非满分):
此份代码思维简单粗暴,空间超限仅为只过了两份数据。
#include<iostream>
#include<vector>
using namespace std;
const int MAX = 100001;//数据量最大为100000。
/*
第一次想法:
简单粗暴,把所有的n个整数两两异或的结果存放到一个n*n的矩阵中
然后在后续的输入中直接查询矩阵就好
这就像是对数据进行分析,将分析后的数据存放,使用的时候在读取
*/
int main()
{
int n, m, x;
cin >> n >> m >> x;
vector<int> a(n);
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
vector<int> l(m);
vector<int> r(m);
for (int i = 0; i < m; i++)
{
cin >> l[i]>>r[i];
}
//定义n*n的结果矩阵,存放结果。
vector<vector<bool>> res(n, vector<bool>(n,false));
//矩阵赋值过程
//a1要与a2,a3,a4...进行异或
//a2要与a3,a4,a5...进行异或
for (int i = 0; i < n; i++)
{
for (int j = i+1; j < n; j++)
{
if ((a[i] ^ a[j]) == x)
{
res[i][j] = true;
}
}
}
//然后是根据该数据集处理问题
for (int i = 0; i < m; i++)
{
//对于每一组数据
bool exit = false;
//需要检查|l[i]到r[i],l[i]到r[i]|这个矩阵中有没有1。
for (int j = l[i] - 1; j <= r[i] - 1; j++)
{
for (int k = j+1; k <= r[i] - 1; k++)
{
if (res[j][k]==true)
{
exit = true;
break;
}
}
if (exit)
break;
}
cout << (exit ? "yes" : "no") << endl;
}
return 0;
}
题解二(非满分):
这次使用了动态规划,但还是不行,因为思路不合适。
#include<iostream>
#include<vector>
using namespace std;
const int MAX = 100001;//数据量最大为100000。
int main()
{
/*
第一种方法很明显十分笨拙,为什么呢?
处理了没必要的数据,我们不必知道那么详细的数据,存放起来
数据处理的过于详细不仅会导致占用空间,而且在使用数据时还要对数据进行在此处理
我们只需要从数据中处理出我们需要的信息,不必那么详细,在使用时能刚好就拿来用。
那么这么想,
对每一个a[i],创建对应的一个整形变量,存放着到那个位置有异或为1。该变量初始化为无限大,然后在输入数据的同时不断更新。
*/
int n, m, x;
cin >> n >> m >> x;
vector<int> a(n);
vector<int> res_a(n,MAX);
for (int i = 0; i < n; i++)
{
cin >> a[i];
//每输入一个数,都要对已输入的数的res_a进行动态的检测修改。
for (int j = 0; j < i; j++)
{
//倘若这个值的所在更小,那么才有检测的必要
if (i < res_a[j])
{
if ((a[j] ^ a[i]) == x)
{
//更新
res_a[j] = i;
}
}
}
}
//此时的res_a里,有每个位置的值自己异或结果为x的最小位置。
//我们想要的,是每个位置异或为x的最小区间。因此我们最后要更新一下这个结果。
for (int i = n - 2; i >= 0; i--)
{
if (res_a[i] > res_a[i+1])
res_a[i] = res_a[i + 1];
}
vector<int> l(m);
vector<int> r(m);
for (int i = 0; i < m; i++)
{
cin >> l[i] >> r[i];
}
for (int i = 0; i < m; i++)
{
if (res_a[l[i] - 1] <= r[i] - 1)
cout << "yes" <<endl;
else
cout << "no" <<endl;
}
return 0;
}
/*这个算法的时间复杂度为O(n²),因此卡了一部分数据。*/
题解三(满分):
也是动态规划,但思路不同,详细请看注释
#include<iostream>
#include<vector>
using namespace std;
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
int n, m, x;
cin >> n >> m >> x;
vector<int> a(n);
//定义dp,其中存放对于每个位置i来说,若i为r,那么期间存在与l异或为x的最小的区间,即最大的l。
//初始化时,要让区间尽量大,于是让l尽量小,我干脆让其为-1
vector<int> dp(n, -1);
//存放每一个a[i]的位置,a[i]的最大为2的20次方,约小于1 100 000
//且初始化值为最小
vector<int> position_of_a(1100000,-1);
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
//i从1开始是因为,第一个的索引为0的数作为r的话,一定不存在yes的情况
//而且,for循环里有需要i-1的地方。
for (int i = 1; i < n; i++)
{
position_of_a[a[i]] = i;
//但是,如果数据中有重复的数据怎么办呢?
//我想,对于每一个a[i],它与x异或,得到目标值。
//我们令a[i]的位置为r,那么目标值的位置一定是在a[i]的左边的。
//那么对于目标值,如果已输入的目标值有多个,那么我一定希望取不大于a[i]中尽量大的,以获得更小的区间。
//而这时,a[i]右边的值的位置信息,我们还没有赋值,被初始化为最小整形,那么我直接取目标位置最大的就好。
//那么,dp里就是这个position_of_a[[i]^x],或者是左边的那个值的可异或区间
dp[i] = max(position_of_a[a[i] ^ x],dp[i-1]);
}
/*检验*/
/*
for (int i = 0; i < n; i++)
{
cout << dp[i] << endl;
}
*/
vector<int> l(m);
vector<int> r(m);
for (int i = 0; i < m; i++)
{
cin >> l[i] >> r[i];
}
for (int i = 0; i < m; i++)
{
if (dp[r[i] - 1] >= l[i] - 1)
cout << "yes"<<'\n';
else
cout << "no" << '\n';
}
return 0;
}
这里要注意“
ios_base::sync_with_stdio(0);
cin.tie(0);
”是必要的,是取消流同步。
或者把输入输出改为scanf,printf,这样不需要这两行也能过。还有在使用这两行代码时不能使用endl换行,而要使用'\n'。
总之,干脆使用scanf,printf吧,输出格式化也方便,不过visual studio 2022里C++使用scanf会报错,换个编译器,或者使用scanf_s就好了。