NOJ [1314] Brave Sword

链接地址:http://ac.nbutoj.com/Problem/view.xhtml?id=1314

是不是想起了筷子大作战这一题?
(1) 那题的意思就是有很多筷子,除了其中一只,其他都是成对等长出现的。
直接异或,所有相同的数都会被抵消(异或运算中,相同位置值相等的结果为0,就是说所有成对出现的都会最终成为0),最后的结果自然是那个落单的数了。

然后,我们来看这题。
(2) 这题的意思是有很多把钥匙,每把钥匙上都有编号。有相同编号的两把钥匙都要依次排除,这样最后肯定剩下2把钥匙,那么这两把钥匙的编号是不一样的,就是答案所要的钥匙。

如果是有两个互不相同的数,我们可以把他分成两个段,段名分别为A,B。这里为了解释方便,给出10个数,分别为a,a,b,b,c,c,d,d,e,f;
 
现在我们知道,只要A段和B段能满足上面的(1)所说的情形,即两个不同的数(e,f分别在A,B段内)分别在不同的段内,其他的数成对出现在相同的段内,就可以仿照(1)得到结果。举个例子A(a,a,b,b,e);B(c,c,d,d,f);
 
是不是觉得很难分呢?怎么才能按照上面的形式分开成为A,B呢?别急,注意咯,我上面说的是其他数成对出现,并不是一定要对半分,也可以是A(a,a,b,b,c,c,d,d,e);B(f);或者A(a,a,b,b,c,c,e);B(d,d,f);是不是有点头绪了?没有?别紧张,往下看,快搞定了
 
我们先来想想如何分e,f。首先,他们本身不相等,所以e^f的值一定不为0,也就是说,e^f的值转换成2进制,一定至少有一个1出现。好的,我们从右向左找第一次出现1的位置,将所有数中,这个位置是1的放进A,是0的放进B。OK,完全符合我们对AB段的要求,再从A,B中分别异或找出e,f不是问题!

出题代码:
#include <set>
#include <map>
#include <list>
#include <stack>
#include <queue>
#include <cmath>
#include <cstdio>
#include <vector>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;

#define N 1000010

int a[N];
int b[N];
int c[N];

int main()
{
	int n;
	
	//freopen("data.in", "r", stdin);
	//freopen("data.out", "w", stdout);
	while (~scanf("%d", &n))
	{
		int s = 0;
		for (int i = 0; i < n; i++)
		{
			scanf("%d", &a[i]);
			s ^= a[i];
		}
		int p;
		for (int i = 0; i < 32; i++)
		{
			if ((s >> i) % 2 == 1)
			{
				p = i;
				break;
			}
		}
		int bn, cn;
		bn = cn = 0;
		for (int i = 0; i < n; i++)
		{
			if ((a[i] >> p) % 2) b[bn++] = a[i];
			else c[cn++] = a[i];
		}
		int s1, s2;
		s1 = s2 = 0;
		for (int i = 0; i < bn; i++)
		{
			s1 ^= b[i];
		}
		for (int i = 0; i < cn; i++)
		{
			s2 ^= c[i];
		}
		printf("%d %d\n", min(s1, s2), max(s1, s2));
	}
	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值