题目1
题意:
给出一组序列,有q次访问,每次访问输入一个x,每次对这组序列异或x,然后输出最小未出现的元素。注意每次访问后数组都会改变。
分析:
由于异或具有结合律,所以不需要每次去改变序列。如何求最小未出现过的元素呢,由于x^y=z,若知道y和z,则x就是固定的。y就是每次的访问,z是答案,因为z不是原数组异或y产生的,所以x必然未在原数组中。所以答案是由未在数组中的元素决定的。所以我们可以用那些未出现过的元素,求他们与x异或的最小值。这样就是用字典树就可以了。
注意这些元素最多可以取到2倍的定义域。证明:若x大于定义域,那么答案显然为0,且x不可能超过定义域的两倍。若x在定义域内,答案也在定义域内,那么所对应的那个不在数组中的值至多是定义域的两倍。
#include <iostream>
using namespace std;
int tire[600005*20][2],tot = 1;
int num[600005];
void insert(int a)
{
int rt = 0;
for (int i = 19; i >= 0; i--)
{
int t = (a>>i) & 1;
if( tire[rt][t] == 0 )
{
tire[rt][t] = tot ++;
}
rt = tire[rt][t];
}
}
int query(int q)
{
int res = 0,rt = 0;
for (int i = 19; i >= 0; i--)
{
int t = (q >> i) & 1;
if( tire[rt][t] == 0 )
{
res += (1<<i);
rt = tire[rt][t^1];
}else rt = tire[rt][t];
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n,m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
num[x] = 1;
}
for (int i = 0; i <= 600000; i++)
{
if( !num[i] ) insert(i);
//cout << i << " Asd" << tot << '\n';
}
int q = 0;
for (int i = 1; i <= m; i++)
{
int x;
cin >> x;
q ^= x;
cout << query(q) << '\n';
}
return 0;
}