N个数间的最小异或对

1,结论:

最小异或对必定是n个点排完序后,相邻两个点产生的异或值中的一个

2,证明:

设a<b<c,我们证明最小异或对只可能是a^b或者b^c,不可能是a^c。

  1. 设c与a高位第一位不同的位为x(x位必定是c为1,a为0)
  2. 那么显然,x位之前的高位a与c都是相同的
  3. b当然x位之前也是与c相同(c为1你必须为1,因为a也为1,你不为1就b<a了,c为0你不能为1,否则b>c了)
  4. 而b在x位可以是1或者0
  5. 所以a^c时x位一定为1,而a^b或者b^c必定有其中一个是x为异或为0(b的x位为1,就是与a异或x位为0),所以a^c不可能最小。得证。

例题:F-最小异或对

思路:

知道结论后就是操作的细节

  1. 每次插入数x,判断是否有大于等于他的数It,有在异或set里插入他们两个的异或。再判断it前面有没有数st,有则x与他异或一遍。如果前后都存在,那么原来st与it的异或值要删除.
  2. 删除数时,我们先把他除去,再判断剩下set里面有没有大于等于他的值与小于他的值,有就删除之间的异或值。如果,左右都有,就补上一个st与it的异或值.。
  3. 最后注意,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;
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值