最近遇到问题,我很难找到答案。这是问题:
考虑一组数字。有树的输入形式:
1 x
2 x
3
第一个命令将整数x添加到集合中。第二个意味着对列表中的每个元素y放置:
y = y xor x
and最后一条命令将打印出该组中最大的数字。例如:
10
3
1 7
3
2 4
2 8
2 3
1 10
1 3
3
2 1
结果:
0
7
15
如果n是输入中的命令数:
到目前为止,我的解决方案:让我们调用集合S并具有一个整数m,该整数最初为0。如您所知:
number = number xor x xor x
意味着如果我们在某事物上两次应用xor,则其效果会相反,并且原始数字不会改变。话虽如此,如果我们每次插入数字(命令1),我们都会执行以下操作:
y = y xor m
add y to S
并且每次我们想要从集合中获取一个数字:
find y
y = y xor m
return y
并且如果命令二出现以下内容:
m = m xor x
然后问题几乎解决了,因为最初保存数字的XORed版本,并且在需要时我们做相反的操作!但是这里的问题是找到集合中的最大数字(请注意集合中的数字与原始数字不同),因此命令3可以正常工作。我不知道如何在有效时间内完成此操作。但是我有一个主意:如果首先将数字的二进制表示形式保存在集合中的[[trie数据结构中,也许我们可以很快找到最大的数字。我真的不知道怎么回事,但是这个主意发生在我身上。总结一下,这就是我的问题:
问题1:
如何在修订后的列表中找到最大的数字问题2:这个trie idea好吗?问题3:我该如何在代码中实现它(这里的语言不是很重要),以便可以在时间找到它?首先解决这个问题还需要多少时间?感谢您阅读我的问题。
想法是将数字存储为二进制表示法,但将最大位放在首位,因此在遍历trie时,我们可以选择一个导致最大答案的分支。
[要确定选择哪个分支,我们可以一点一点地确定,如果从某个特里节点中,我们有2个分支,其值分别为0和1,则选择与m异或后结果更好的分支
示例代码(C ++):
#include <bits/stdc++.h>
using namespace std;
int Trie[4000005][2];
int nxt = 2;
void Add(int x)
{
bitset<32>b(x);
int c = 1;
for(int j=31; j>=0; j--)
if(Trie[c][b[j]])c=Trie[c][b[j]];
else c = Trie[c][b[j]] = nxt++;
}
int Get(int x)
{
bitset<32>b(x),res(0);
int c = 1;
for(int j=31; j>=0; j--)
if(Trie[c][!b[j]])c=Trie[c][!b[j]],res[j]=!b[j];
else c = Trie[c][b[j]], res[j]=b[j];
return res.to_ullong()^x;
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int q,m=0;
cin>>q;
Add(0);
while(q--)
{
int type;
cin>>type;
if(type==1)
{
int x;
cin>>x;
Add(x^m);
}
else if(type==2)
{
int x;
cin>>x;
m^=x;
}
else cout<<Get(m)<<"\n";
}
}
x
的位数是恒定的(对于10 ^ 9,您将不得不查看最低的30位)。m = 0
时,每次遇到第二条命令时,您都会执行m ^= x
(m = m xor x)。 *
/ \
1 1 there are numbers with highest bit 0 and 1
/ /
1 1 of the numbers with 1st bit 0, there is a number with 2nd bit 0 and ...
\ / \
1 1 1 of the numbers with 1st and 2nd bit 0, there is a number with 3rd bit 1,...
1 4 5 (the numbers just to clarify)
所以添加数字仅意味着添加一些边和节点。
x
:n
初始化为树的根,i = 29
我们正在查看的m
的位以及解决方案x = 0
。mi = (m & (1 << i)) >> i
(如果m
中的位为1,则为1,否则为0。n
时只有一条边表示0,或者如果mi == 1
只有一条边,则我们有一个0边:n
成为由该边连接的节点,x = 2 * x + mi
(或更多)花式:x = (x << 1) | mi
)。n
成为由1边和x = 2 * x + 1 - mi
]连接的节点>如果i > 0
:将i
减1,然后继续执行步骤2。