Vitya and Strange Lesson
Vitya and Strange Lesson
https://ac.nowcoder.com/acm/problem/112209
题意:
给定长度为的序列
,
次操作,每次对序列
的所有数都异或上一个数
,问每次异或后,没有出现在序列
中的最小自然数的值。
数据范围:
考虑本问题时,当序列异或上
得到
序列时,初始状态中不存在于
序列中的所有数一起组成的序列
异或上
得到
序列时,
和
一定也是无交集的。
题解1:
所以问题转化为将所有不存在于序列中的自然数插入
树中,每次查询时只需要对
,找到其中与
异或起来得到的值最小的数,即为答案。这里的
是异或上前
次询问后的值的
。
代码1:
#include<bits/stdc++.h>
using namespace std;
const int N = 6e5 + 10;
int son[N * 21][2], idx;
int st[N * 21];
int n, m;
void insert(int x) {
int p = 0;
for(int i = 20; i >= 0; --i) {
int u = x >> i & 1;
if(!son[p][u]) son[p][u] = ++idx;
p = son[p][u];
}
}
int query(int x) {
int res = 0, p = 0;
for(int i = 20; i >= 0; --i) {
int u = x >> i & 1;
if(son[p][u]) res = res * 2 + u, p = son[p][u];
else res = res * 2 + !u, p = son[p][!u];
}
return res;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) {
int x;
scanf("%d", &x);
st[x] = true;
}
//将所有初始不在a中的元素加入
for(int i = 0; i < N; ++i)
if(!st[i]) insert(i);
//对于当前的trie树中的元素,如果其不在a中,则对应其中可以求出一个最小的
int ans = 0;
for(int i = 1; i <= m; ++i) {
int x;
scanf("%d", &x);
ans ^= x;
printf("%d\n", query(ans) ^ ans);
}
return 0;
} 题解2:
考虑将序列插入
树,同时记录每个结点被使用的次数,考虑到一个结点被使用的次数为最多为其
次。所以在查询时只要看当前优先选择的数是否被使用了
次即可,如果没有则优先选择,否则被迫选择另一边。而优先选择的条件一定是与当前的值二进制相同的一边,保证异或后答案尽可能小。
代码2:
#include<bits/stdc++.h>
using namespace std;
const int N = 6e5 + 10;
int ok[N];
int son[N * 32][2], cnt[N * 32], idx;
int n, m;
void insert(int x) {
int p = 0;
for(int i = 20; i >= 0; --i) {
int u = x >> i & 1;
if(!son[p][u]) son[p][u] = ++idx;
p = son[p][u];
++cnt[p];
}
}
int query(int x) {
int p = 0, res = 0;
for(int i = 20; i >= 0; --i) {
int u = x >> i & 1;
if(cnt[son[p][u]] == (1 << i)) res = res * 2 + !u, p = son[p][!u];
else res = res * 2 + u, p = son[p][u];
}
return res;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) {
int x; scanf("%d", &x);
if(ok[x]) continue;
ok[x] = true;
insert(x);
}
int ans = 0;
while(m--) {
int x; scanf("%d", &x);
ans ^= x;
printf("%d\n", query(ans) ^ ans);
}
return 0;
}
查看14道真题和解析