1,结论:
最小异或对必定是n个点排完序后,相邻两个点产生的异或值中的一个
2,证明:
设a<b<c,我们证明最小异或对只可能是a^b或者b^c,不可能是a^c。
- 设c与a高位第一位不同的位为x(x位必定是c为1,a为0)
- 那么显然,x位之前的高位a与c都是相同的
- b当然x位之前也是与c相同(c为1你必须为1,因为a也为1,你不为1就b<a了,c为0你不能为1,否则b>c了)
- 而b在x位可以是1或者0
- 所以a^c时x位一定为1,而a^b或者b^c必定有其中一个是x为异或为0(b的x位为1,就是与a异或x位为0),所以a^c不可能最小。得证。
例题:F-最小异或对
思路:
知道结论后就是操作的细节
- 每次插入数x,判断是否有大于等于他的数It,有在异或set里插入他们两个的异或。再判断it前面有没有数st,有则x与他异或一遍。如果前后都存在,那么原来st与it的异或值要删除.
- 删除数时,我们先把他除去,再判断剩下set里面有没有大于等于他的值与小于他的值,有就删除之间的异或值。如果,左右都有,就补上一个st与it的异或值.。
- 最后注意,multiset删除数不要用erase取删除这个数(会无论这个数存在多少个都删除),我们只要删除其中一个就好,所以删除指针。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl "\n"
void mysolve()
{
multiset<int>a,ans;
string s;
int n,x;
cin>>n;
while (n--)
{
cin>>s;
if (s=="ADD")
{
cin>>x;
//插入x前先判断原set有没有他前后的数
auto it=a.lower_bound(x);
if (it!=a.end())ans.insert(x^*it);
if (it!=a.begin())ans.insert(x^*prev(it));
if (it!=a.end()&&it!=a.begin())ans.erase(ans.find(*it^*prev(it)));
a.insert(x);//最后插入
}
else if (s=="DEL")
{
cin>>x;
a.erase(a.find(x));//删除是删除迭代器
//删除后再判断set有没有其左右的数
auto it=a.lower_bound(x);
if (it!=a.end())ans.erase(ans.find(x^*it));
if (it!=a.begin())ans.erase(ans.find(x^*prev(it)));
if (it!=a.end()&&it!=a.begin())ans.insert(*it^*prev(it));
}
else
{
cout<<*ans.begin()<<endl;
}
}
}
int32_t main()
{
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
mysolve();
system("pause");
return 0;
}